From faf64ac13cae57d1e9be770b38125cb64079ea0f Mon Sep 17 00:00:00 2001 From: Masatoshi Itagaki Date: Fri, 12 Apr 2019 11:59:57 +0900 Subject: [PATCH 1/2] added translated files in site/ja/r2/tutorials/keras --- .../keras/basic_classification.ipynb | 1021 +++++++++++++++++ .../r2/tutorials/keras/basic_regression.ipynb | 866 ++++++++++++++ .../keras/basic_text_classification.ipynb | 726 ++++++++++++ .../r2/tutorials/keras/feature_columns.ipynb | 731 ++++++++++++ site/ja/r2/tutorials/keras/index.md | 22 + .../keras/overfit_and_underfit.ipynb | 724 ++++++++++++ .../keras/save_and_restore_models.ipynb | 876 ++++++++++++++ 7 files changed, 4966 insertions(+) create mode 100644 site/ja/r2/tutorials/keras/basic_classification.ipynb create mode 100644 site/ja/r2/tutorials/keras/basic_regression.ipynb create mode 100644 site/ja/r2/tutorials/keras/basic_text_classification.ipynb create mode 100644 site/ja/r2/tutorials/keras/feature_columns.ipynb create mode 100644 site/ja/r2/tutorials/keras/index.md create mode 100644 site/ja/r2/tutorials/keras/overfit_and_underfit.ipynb create mode 100644 site/ja/r2/tutorials/keras/save_and_restore_models.ipynb diff --git a/site/ja/r2/tutorials/keras/basic_classification.ipynb b/site/ja/r2/tutorials/keras/basic_classification.ipynb new file mode 100644 index 00000000000..a74d52d2094 --- /dev/null +++ b/site/ja/r2/tutorials/keras/basic_classification.ipynb @@ -0,0 +1,1021 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "MhoQ0WE77laV" + }, + "source": [ + "##### Copyright 2018 The TensorFlow Authors." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "cellView": "form", + "colab": {}, + "colab_type": "code", + "id": "_ckMIh7O7s6D" + }, + "outputs": [], + "source": [ + "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "cellView": "form", + "colab": {}, + "colab_type": "code", + "id": "vasWnqRgy1H4" + }, + "outputs": [], + "source": [ + "#@title MIT License\n", + "#\n", + "# Copyright (c) 2017 François Chollet\n", + "#\n", + "# Permission is hereby granted, free of charge, to any person obtaining a\n", + "# copy of this software and associated documentation files (the \"Software\"),\n", + "# to deal in the Software without restriction, including without limitation\n", + "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n", + "# and/or sell copies of the Software, and to permit persons to whom the\n", + "# Software is furnished to do so, subject to the following conditions:\n", + "#\n", + "# The above copyright notice and this permission notice shall be included in\n", + "# all copies or substantial portions of the Software.\n", + "#\n", + "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n", + "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n", + "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n", + "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n", + "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n", + "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n", + "# DEALINGS IN THE SOFTWARE." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "jYysdyb-CaWM" + }, + "source": [ + "# はじめてのニューラルネットワーク:分類問題の初歩" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "S5Uhzt6vVIB2" + }, + "source": [ + "\n", + " \n", + " \n", + " \n", + "
\n", + " View on TensorFlow.org\n", + " \n", + " Run in Google Colab\n", + " \n", + " View source on GitHub\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "FbVhjPpzn6BM" + }, + "source": [ + "このガイドでは、スニーカーやシャツなど、身に着けるものの写真を分類するニューラルネットワークのモデルを訓練します。すべての詳細を理解できなくても問題ありません。TensorFlowの全体を早足で掴むためのもので、詳細についてはあとから見ていくことになります。\n", + "\n", + "このガイドでは、TensorFlowのモデルを構築し訓練するためのハイレベルのAPIである [tf.keras](https://www.tensorflow.org/guide/keras)を使用します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "jL3OqFKZ9dFg" + }, + "outputs": [], + "source": [ + "!pip install tensorflow==2.0.0-alpha0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "dzLKpmZICaWN" + }, + "outputs": [], + "source": [ + "from __future__ import absolute_import, division, print_function\n", + "\n", + "# TensorFlow と tf.keras のインポート\n", + "import tensorflow as tf\n", + "from tensorflow import keras\n", + "\n", + "# ヘルパーライブラリのインポート\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "\n", + "print(tf.__version__)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "yR0EdgrLCaWR" + }, + "source": [ + "## ファッションMNISTデータセットのロード" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "DLdCchMdCaWQ" + }, + "source": [ + "このガイドでは、[Fashion MNIST](https://github.com/zalandoresearch/fashion-mnist)を使用します。Fashion MNISTには10カテゴリーの白黒画像70,000枚が含まれています。それぞれは下図のような1枚に付き1種類の衣料品が写っている低解像度(28×28ピクセル)の画像です。\n", + "\n", + "\n", + " \n", + " \n", + "
\n", + " \"Fashion\n", + "
\n", + " Figure 1. Fashion-MNIST samples (by Zalando, MIT License).
 \n", + "
\n", + "\n", + "Fashion MNISTは、画像処理のための機械学習での\"Hello, World\"としてしばしば登場する[MNIST](http://yann.lecun.com/exdb/mnist/) データセットの代替として開発されたものです。MNISTデータセットは手書きの数字(0, 1, 2 など)から構成されており、そのフォーマットはこれから使うFashion MNISTと全く同じです。\n", + "\n", + "Fashion MNISTを使うのは、目先を変える意味もありますが、普通のMNISTよりも少しだけ手応えがあるからでもあります。どちらのデータセットも比較的小さく、アルゴリズムが期待したとおりに機能するかどうかを確かめるために使われます。プログラムのテストやデバッグのためには、よい出発点になります。\n", + "\n", + "ここでは、60,000枚の画像を訓練に、10,000枚の画像を、ネットワークが学習した画像分類の正確性を評価するのに使います。TensorFlowを使うと、下記のようにFashion MNISTのデータを簡単にインポートし、ロードすることが出来ます。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "7MqDQO0KCaWS" + }, + "outputs": [], + "source": [ + "fashion_mnist = keras.datasets.fashion_mnist\n", + "\n", + "(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "t9FDsUlxCaWW" + }, + "source": [ + "ロードしたデータセットは、NumPy配列になります。\n", + "\n", + "* `train_images` と `train_labels` の2つの配列は、モデルの訓練に使用される**訓練用データセット**です。\n", + "* 訓練されたモデルは、 `test_images` と `test_labels` 配列からなる**テスト用データセット**を使ってテストします。\n", + "\n", + "画像は28×28のNumPy配列から構成されています。それぞれのピクセルの値は0から255の間の整数です。**ラベル**(label)は、0から9までの整数の配列です。それぞれの数字が下表のように、衣料品の**クラス**(class)に対応しています。\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
LabelClass
0T-shirt/top
1Trouser
2Pullover
3Dress
4Coat
5Sandal
6Shirt
7Sneaker
8Bag
9Ankle boot
\n", + "\n", + "画像はそれぞれ単一のラベルに分類されます。データセットには上記の**クラス名**が含まれていないため、後ほど画像を出力するときのために、クラス名を保存しておきます。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "IjnLH5S2CaWx" + }, + "outputs": [], + "source": [ + "class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', \n", + " 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Brm0b_KACaWX" + }, + "source": [ + "## データの観察\n", + "\n", + "モデルの訓練を行う前に、データセットのフォーマットを見てみましょう。下記のように、訓練用データセットには28×28ピクセルの画像が60,000枚含まれています。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "zW5k_xz1CaWX" + }, + "outputs": [], + "source": [ + "train_images.shape" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "cIAcvQqMCaWf" + }, + "source": [ + "同様に、訓練用データセットには60,000個のラベルが含まれます。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "TRFYHB2mCaWb" + }, + "outputs": [], + "source": [ + "len(train_labels)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "YSlYxFuRCaWk" + }, + "source": [ + "ラベルはそれぞれ、0から9までの間の整数です。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "XKnCTHz4CaWg" + }, + "outputs": [], + "source": [ + "train_labels" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "TMPI88iZpO2T" + }, + "source": [ + "テスト用データセットには、10,000枚の画像が含まれます。画像は28×28ピクセルで構成されています。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "2KFnYlcwCaWl" + }, + "outputs": [], + "source": [ + "test_images.shape" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "rd0A0Iu0CaWq" + }, + "source": [ + "テスト用データセットには10,000個のラベルが含まれます。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "iJmPr5-ACaWn" + }, + "outputs": [], + "source": [ + "len(test_labels)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "ES6uQoLKCaWr" + }, + "source": [ + "## データの前処理\n", + "\n", + "ネットワークを訓練する前に、データを前処理する必要があります。最初の画像を調べてみればわかるように、ピクセルの値は0から255の間の数値です。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "m4VEw8Ud9Quh" + }, + "outputs": [], + "source": [ + "plt.figure()\n", + "plt.imshow(train_images[0])\n", + "plt.colorbar()\n", + "plt.grid(False)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "3jCZdQNNCaWv" + }, + "source": [ + "ニューラルネットワークにデータを投入する前に、これらの値を0から1までの範囲にスケールします。そのためには、画素の値を255で割ります。\n", + "\n", + "**訓練用データセット**と**テスト用データセット**は、同じように前処理することが重要です。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "bW5WzIPlCaWv" + }, + "outputs": [], + "source": [ + "train_images = train_images / 255.0\n", + "\n", + "test_images = test_images / 255.0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Ee638AlnCaWz" + }, + "source": [ + "**訓練用データセット**の最初の25枚の画像を、クラス名付きで表示してみましょう。ネットワークを構築・訓練する前に、データが正しいフォーマットになっていることを確認します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "oZTImqg_CaW1" + }, + "outputs": [], + "source": [ + "plt.figure(figsize=(10,10))\n", + "for i in range(25):\n", + " plt.subplot(5,5,i+1)\n", + " plt.xticks([])\n", + " plt.yticks([])\n", + " plt.grid(False)\n", + " plt.imshow(train_images[i], cmap=plt.cm.binary)\n", + " plt.xlabel(class_names[train_labels[i]])\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "59veuiEZCaW4" + }, + "source": [ + "## モデルの構築\n", + "\n", + "ニューラルネットワークを構築するには、まずモデルの階層を定義し、その後モデルをコンパイルします。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Gxg1XGm0eOBy" + }, + "source": [ + "### 層の設定\n", + "\n", + "ニューラルネットワークを形作る基本的な構成要素は**層**(layer)です。層は、入力されたデータから「表現」を抽出します。それらの「表現」は、今取り組もうとしている問題に対して、より「意味のある」ものであることが期待されます。\n", + "\n", + "ディープラーニングモデルのほとんどは、単純な層の積み重ねで構成されています。`tf.keras.layers.Dense` のような層のほとんどには、訓練中に学習されるパラメータが存在します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "9ODch-OFCaW4" + }, + "outputs": [], + "source": [ + "model = keras.Sequential([\n", + " keras.layers.Flatten(input_shape=(28, 28)),\n", + " keras.layers.Dense(128, activation='relu'),\n", + " keras.layers.Dense(10, activation='softmax')\n", + "])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "gut8A_7rCaW6" + }, + "source": [ + "このネットワークの最初の層は、`tf.keras.layers.Flatten` です。この層は、画像を(28×28ピクセルの)2次元配列から、28×28=784ピクセルの、1次元配列に変換します。この層が、画像の中に積まれているピクセルの行を取り崩し、横に並べると考えてください。この層には学習すべきパラメータはなく、ただデータのフォーマット変換を行うだけです。\n", + "\n", + "ピクセルが1次元化されたあと、ネットワークは2つの `tf.keras.layers.Dense` 層となります。これらの層は、密結合あるいは全結合されたニューロンの層となります。最初の `Dense` 層には、128個のノード(あるはニューロン)があります。最後の層でもある2番めの層は、10ノードの**softmax**層です。この層は、合計が1になる10個の確率の配列を返します。それぞれのノードは、今見ている画像が10個のクラスのひとつひとつに属する確率を出力します。\n", + "\n", + "### モデルのコンパイル\n", + "\n", + "モデルが訓練できるようになるには、いくつかの設定を追加する必要があります。それらの設定は、モデルの**コンパイル**(compile)時に追加されます。\n", + "\n", + "* **損失関数**(loss function) —訓練中にモデルがどれくらい正確かを測定します。この関数の値を最小化することにより、訓練中のモデルを正しい方向に向かわせようというわけです。\n", + "* **オプティマイザ**(optimizer)—モデルが見ているデータと、損失関数の値から、どのようにモデルを更新するかを決定します。\n", + "* **メトリクス**(metrics) —訓練とテストのステップを監視するのに使用します。下記の例では*accuracy* (正解率)、つまり、画像が正しく分類された比率を使用しています。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "Lhan11blCaW7" + }, + "outputs": [], + "source": [ + "model.compile(optimizer='adam', \n", + " loss='sparse_categorical_crossentropy',\n", + " metrics=['accuracy'])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "qKF6uW-BCaW-" + }, + "source": [ + "## モデルの訓練\n", + "\n", + "ニューラルネットワークの訓練には次のようなステップが必要です。\n", + "\n", + "1. モデルに訓練用データを投入します—この例では `train_images` と `train_labels` の2つの配列です。\n", + "2. モデルは、画像とラベルの対応関係を学習します。\n", + "3. モデルにテスト用データセットの予測(分類)を行わせます—この例では `test_images` 配列です。その後、予測結果と `test_labels` 配列を照合します。 \n", + "\n", + "訓練を開始するには、`model.fit` メソッドを呼び出します。モデルを訓練用データに \"fit\"(適合)させるという意味です。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "xvwvpA64CaW_" + }, + "outputs": [], + "source": [ + "model.fit(train_images, train_labels, epochs=5)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "W3ZVOhugCaXA" + }, + "source": [ + "モデルの訓練の進行とともに、損失値と正解率が表示されます。このモデルの場合、訓練用データでは0.88(すなわち88%)の正解率に達します。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "oEw4bZgGCaXB" + }, + "source": [ + "## 正解率の評価\n", + "\n", + "次に、テスト用データセットに対するモデルの性能を比較します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "VflXLEeECaXC" + }, + "outputs": [], + "source": [ + "test_loss, test_acc = model.evaluate(test_images, test_labels)\n", + "\n", + "print('\\nTest accuracy:', test_acc)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "yWfgsmVXCaXG" + }, + "source": [ + "ご覧の通り、テスト用データセットでの正解率は、訓練用データセットでの正解率よりも少し低くなります。この訓練時の正解率とテスト時の正解率の差は、**過学習**(over fitting)の一例です。過学習とは、新しいデータに対する機械学習モデルの性能が、訓練時と比較して低下する現象です。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "xsoS7CPDCaXH" + }, + "source": [ + "## 予測する\n", + "\n", + "モデルの訓練が終わったら、そのモデルを使って画像の分類予測を行うことが出来ます。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "Gl91RPhdCaXI" + }, + "outputs": [], + "source": [ + "predictions = model.predict(test_images)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "x9Kk1voUCaXJ" + }, + "source": [ + "これは、モデルがテスト用データセットの画像のひとつひとつを分類予測した結果です。最初の予測を見てみましょう。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "3DmJEUinCaXK" + }, + "outputs": [], + "source": [ + "predictions[0]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "-hw1hgeSCaXN" + }, + "source": [ + "予測結果は、10個の数字の配列です。これは、その画像が10の衣料品の種類のそれぞれに該当するかの「確信度」を表しています。どのラベルが一番確信度が高いかを見てみましょう。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "qsqenuPnCaXO" + }, + "outputs": [], + "source": [ + "np.argmax(predictions[0])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "E51yS7iCCaXO" + }, + "source": [ + "というわけで、このモデルは、この画像が、アンクルブーツ、`class_names[9]` である可能性が最も高いと判断したことになります。これが正しいかどうか、テスト用ラベルを見てみましょう。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "Sd7Pgsu6CaXP" + }, + "outputs": [], + "source": [ + "test_labels[0]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "kgdvGD52CaXR" + }, + "source": [ + "10チャンネルすべてをグラフ化してみることができます。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "VsRq6uZiG7eT" + }, + "outputs": [], + "source": [ + "def plot_image(i, predictions_array, true_label, img):\n", + " predictions_array, true_label, img = predictions_array[i], true_label[i], img[i]\n", + " plt.grid(False)\n", + " plt.xticks([])\n", + " plt.yticks([])\n", + "\n", + " plt.imshow(img, cmap=plt.cm.binary)\n", + "\n", + " predicted_label = np.argmax(predictions_array)\n", + " if predicted_label == true_label:\n", + " color = 'blue'\n", + " else:\n", + " color = 'red'\n", + "\n", + " plt.xlabel(\"{} {:2.0f}% ({})\".format(class_names[predicted_label],\n", + " 100*np.max(predictions_array),\n", + " class_names[true_label]),\n", + " color=color)\n", + "\n", + "def plot_value_array(i, predictions_array, true_label):\n", + " predictions_array, true_label = predictions_array[i], true_label[i]\n", + " plt.grid(False)\n", + " plt.xticks([])\n", + " plt.yticks([])\n", + " thisplot = plt.bar(range(10), predictions_array, color=\"#777777\")\n", + " plt.ylim([0, 1]) \n", + " predicted_label = np.argmax(predictions_array)\n", + "\n", + " thisplot[predicted_label].set_color('red')\n", + " thisplot[true_label].set_color('blue')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "aZ_jDyLZG7eW" + }, + "source": [ + "0番目の画像と、予測、予測配列を見てみましょう。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "UH_jgCxEG7eW" + }, + "outputs": [], + "source": [ + "i = 0\n", + "plt.figure(figsize=(6,3))\n", + "plt.subplot(1,2,1)\n", + "plot_image(i, predictions, test_labels, test_images)\n", + "plt.subplot(1,2,2)\n", + "plot_value_array(i, predictions, test_labels)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "5_7K0ZL7G7eY" + }, + "outputs": [], + "source": [ + "i = 12\n", + "plt.figure(figsize=(6,3))\n", + "plt.subplot(1,2,1)\n", + "plot_image(i, predictions, test_labels, test_images)\n", + "plt.subplot(1,2,2)\n", + "plot_value_array(i, predictions, test_labels)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Lduh0pbfG7eb" + }, + "source": [ + "予測の中のいくつかの画像を、予測値とともに表示してみましょう。正しい予測は青で、誤っている予測は赤でラベルを表示します。数字は予測したラベルのパーセント(100分率)を示します。自信があるように見えても間違っていることがあることに注意してください。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "YGBDAiziCaXR" + }, + "outputs": [], + "source": [ + "# X個のテスト画像、予測されたラベル、正解ラベルを表示します。\n", + "# 正しい予測は青で、間違った予測は赤で表示しています。\n", + "num_rows = 5\n", + "num_cols = 3\n", + "num_images = num_rows*num_cols\n", + "plt.figure(figsize=(2*2*num_cols, 2*num_rows))\n", + "for i in range(num_images):\n", + " plt.subplot(num_rows, 2*num_cols, 2*i+1)\n", + " plot_image(i, predictions, test_labels, test_images)\n", + " plt.subplot(num_rows, 2*num_cols, 2*i+2)\n", + " plot_value_array(i, predictions, test_labels)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "R32zteKHCaXT" + }, + "source": [ + "最後に、訓練済みモデルを使って1枚の画像に対する予測を行います。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "yRJ7JU7JCaXT" + }, + "outputs": [], + "source": [ + "# テスト用データセットから画像を1枚取り出す\n", + "img = test_images[0]\n", + "\n", + "print(img.shape)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "vz3bVp21CaXV" + }, + "source": [ + "`tf.keras` モデルは、サンプルの中の**バッチ**(batch)あるいは「集まり」について予測を行うように作られています。そのため、1枚の画像を使う場合でも、リスト化する必要があります。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "lDFh5yF_CaXW" + }, + "outputs": [], + "source": [ + "# 画像を1枚だけのバッチのメンバーにする\n", + "img = (np.expand_dims(img,0))\n", + "\n", + "print(img.shape)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "EQ5wLTkcCaXY" + }, + "source": [ + "そして、予測を行います。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "o_rzNSdrCaXY", + "scrolled": true + }, + "outputs": [], + "source": [ + "predictions_single = model.predict(img)\n", + "\n", + "print(predictions_single)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "6o3nwO-KG7ex" + }, + "outputs": [], + "source": [ + "plot_value_array(0, predictions_single, test_labels)\n", + "_ = plt.xticks(range(10), class_names, rotation=45)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "cU1Y2OAMCaXb" + }, + "source": [ + "`model.predict` メソッドの戻り値は、リストのリストです。リストの要素のそれぞれが、バッチの中の画像に対応します。バッチの中から、(といってもバッチの中身は1つだけですが)予測を取り出します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "2tRmdq_8CaXb" + }, + "outputs": [], + "source": [ + "np.argmax(predictions_single[0])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "YFc2HbEVCaXd" + }, + "source": [ + "というわけで、モデルは9というラベルを予測しました。" + ] + } + ], + "metadata": { + "colab": { + "collapsed_sections": [], + "name": "basic_classification.ipynb", + "private_outputs": true, + "provenance": [], + "toc_visible": true, + "version": "0.3.2" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/site/ja/r2/tutorials/keras/basic_regression.ipynb b/site/ja/r2/tutorials/keras/basic_regression.ipynb new file mode 100644 index 00000000000..5f097f637f8 --- /dev/null +++ b/site/ja/r2/tutorials/keras/basic_regression.ipynb @@ -0,0 +1,866 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "FhGuhbZ6M5tl" + }, + "source": [ + "##### Copyright 2018 The TensorFlow Authors." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "cellView": "form", + "colab": {}, + "colab_type": "code", + "id": "AwOEIRJC6Une" + }, + "outputs": [], + "source": [ + "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "cellView": "form", + "colab": {}, + "colab_type": "code", + "id": "KyPEtTqk6VdG" + }, + "outputs": [], + "source": [ + "#@title MIT License\n", + "#\n", + "# Copyright (c) 2017 François Chollet\n", + "#\n", + "# Permission is hereby granted, free of charge, to any person obtaining a\n", + "# copy of this software and associated documentation files (the \"Software\"),\n", + "# to deal in the Software without restriction, including without limitation\n", + "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n", + "# and/or sell copies of the Software, and to permit persons to whom the\n", + "# Software is furnished to do so, subject to the following conditions:\n", + "#\n", + "# The above copyright notice and this permission notice shall be included in\n", + "# all copies or substantial portions of the Software.\n", + "#\n", + "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n", + "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n", + "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n", + "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n", + "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n", + "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n", + "# DEALINGS IN THE SOFTWARE." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "EIdT9iu_Z4Rb" + }, + "source": [ + "# 回帰:燃費を予測する " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "bBIlTPscrIT9" + }, + "source": [ + "\n", + " \n", + " \n", + " \n", + "
\n", + " View on TensorFlow.org\n", + " \n", + " Run in Google Colab\n", + " \n", + " View source on GitHub\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "AHp3M9ZmrIxj" + }, + "source": [ + "回帰問題では、価格や確率といった連続的な値の出力を予測することが目的となります。これは、分類問題の目的が、(例えば、写真にリンゴが写っているかオレンジが写っているかといった)離散的なラベルを予測することであるのとは対照的です。\n", + "\n", + "このノートブックでは、古典的な[Auto MPG](https://archive.ics.uci.edu/ml/datasets/auto+mpg)データセットを使用し、1970年代後半から1980年台初めの自動車の燃費を予測するモデルを構築します。この目的のため、モデルにはこの時期の多数の自動車の仕様を読み込ませます。仕様には、気筒数、排気量、馬力、重量などが含まれています。\n", + "\n", + "このサンプルでは`tf.keras` APIを使用しています。詳細は[このガイド](https://www.tensorflow.org/guide/keras)を参照してください。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "moB4tpEHxKB3" + }, + "outputs": [], + "source": [ + "# ペアプロットのためseabornを使用します\n", + "!pip install seaborn" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "1rRo8oNqZ-Rj" + }, + "outputs": [], + "source": [ + "from __future__ import absolute_import, division, print_function\n", + "\n", + "import pathlib\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import pandas as pd\n", + "import seaborn as sns\n", + "\n", + "!pip install tensorflow==2.0.0-alpha0\n", + "import tensorflow as tf\n", + "\n", + "from tensorflow import keras\n", + "from tensorflow.keras import layers\n", + "\n", + "print(tf.__version__)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "F_72b0LCNbjx" + }, + "source": [ + "## Auto MPG データセット\n", + "\n", + "このデータセットは[UCI Machine Learning Repository](https://archive.ics.uci.edu/)から入手可能です。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "gFh9ne3FZ-On" + }, + "source": [ + "### データの取得\n", + "\n", + "まず、データセットをダウンロードします。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "p9kxxgzvzlyz" + }, + "outputs": [], + "source": [ + "dataset_path = keras.utils.get_file(\"auto-mpg.data\", \"https://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data\")\n", + "dataset_path" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "nslsRLh7Zss4" + }, + "source": [ + "pandasを使ってデータをインポートします。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "CiX2FI4gZtTt" + }, + "outputs": [], + "source": [ + "column_names = ['MPG','Cylinders','Displacement','Horsepower','Weight',\n", + " 'Acceleration', 'Model Year', 'Origin'] \n", + "raw_dataset = pd.read_csv(dataset_path, names=column_names,\n", + " na_values = \"?\", comment='\\t',\n", + " sep=\" \", skipinitialspace=True)\n", + "\n", + "dataset = raw_dataset.copy()\n", + "dataset.tail()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "3MWuJTKEDM-f" + }, + "source": [ + "### データのクレンジング\n", + "\n", + "このデータには、いくつか欠損値があります。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "JEJHhN65a2VV" + }, + "outputs": [], + "source": [ + "dataset.isna().sum()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "9UPN0KBHa_WI" + }, + "source": [ + "この最初のチュートリアルでは簡単化のためこれらの行を削除します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "4ZUDosChC1UN" + }, + "outputs": [], + "source": [ + "dataset = dataset.dropna()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "8XKitwaH4v8h" + }, + "source": [ + "`\"Origin\"`の列は数値ではなくカテゴリーです。このため、ワンホットエンコーディングを行います。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "gWNTD2QjBWFJ" + }, + "outputs": [], + "source": [ + "origin = dataset.pop('Origin')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "ulXz4J7PAUzk" + }, + "outputs": [], + "source": [ + "dataset['USA'] = (origin == 1)*1.0\n", + "dataset['Europe'] = (origin == 2)*1.0\n", + "dataset['Japan'] = (origin == 3)*1.0\n", + "dataset.tail()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Cuym4yvk76vU" + }, + "source": [ + "### データを訓練用セットとテスト用セットに分割\n", + "\n", + "データセットを訓練用セットとテスト用セットに分割しましょう。\n", + "\n", + "テスト用データセットは、作成したモデルの最終評価に使用します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "qn-IGhUE7_1H" + }, + "outputs": [], + "source": [ + "train_dataset = dataset.sample(frac=0.8,random_state=0)\n", + "test_dataset = dataset.drop(train_dataset.index)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "J4ubs136WLNp" + }, + "source": [ + "### データの観察\n", + "\n", + "訓練用セットのいくつかの列の組み合わせの同時分布を見てみましょう。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "oRKO_x8gWKv-" + }, + "outputs": [], + "source": [ + "sns.pairplot(train_dataset[[\"MPG\", \"Cylinders\", \"Displacement\", \"Weight\"]], diag_kind=\"kde\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "gavKO_6DWRMP" + }, + "source": [ + "全体の統計値も見てみましょう。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "yi2FzC3T21jR" + }, + "outputs": [], + "source": [ + "train_stats = train_dataset.describe()\n", + "train_stats.pop(\"MPG\")\n", + "train_stats = train_stats.transpose()\n", + "train_stats" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Db7Auq1yXUvh" + }, + "source": [ + "### ラベルと特徴量の分離\n", + "\n", + "ラベル、すなわち目的変数を特徴量から切り離しましょう。このラベルは、モデルに予測させたい数量です。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "t2sluJdCW7jN" + }, + "outputs": [], + "source": [ + "train_labels = train_dataset.pop('MPG')\n", + "test_labels = test_dataset.pop('MPG')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "mRklxK5s388r" + }, + "source": [ + "### データの正規化\n", + "\n", + "上の`train_stats`のブロックをもう一度見て、それぞれの特徴量の範囲がどれほど違っているかに注目してください。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "-ywmerQ6dSox" + }, + "source": [ + "スケールや値の範囲が異なる特徴量を正規化するのは良い習慣です。特徴量の正規化なしでもモデルは収束する**かもしれませんが**、モデルの訓練はより難しくなり、結果として得られたモデルも入力で使われる単位に依存することになります。\n", + "\n", + "注:(正規化に使用する)統計量は意図的に訓練用データセットだけを使って算出していますが、これらはテスト用データセットの正規化にも使うことになります。テスト用のデータセットを、モデルの訓練に使用した分布と同じ分布に射影する必要があるのです。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "JlC5ooJrgjQF" + }, + "outputs": [], + "source": [ + "def norm(x):\n", + " return (x - train_stats['mean']) / train_stats['std']\n", + "normed_train_data = norm(train_dataset)\n", + "normed_test_data = norm(test_dataset)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "BuiClDk45eS4" + }, + "source": [ + "この正規化したデータを使ってモデルを訓練することになります。\n", + "\n", + "注意:ここで入力の正規化に使った統計量(平均と標準偏差)は、先程実施したワンホットエンコーディングとともに、モデルに供給する他のどんなデータにも適用する必要があります。テスト用データセットだけでなく、モデルを本番で使用する際の生のデータも同様です。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "SmjdzxKzEu1-" + }, + "source": [ + "## モデル" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "6SWtkIjhrZwa" + }, + "source": [ + "### モデルの構築\n", + "\n", + "それではモデルを構築しましょう。ここでは、2つの全結合の隠れ層と、1つの連続値を返す出力層からなる、`Sequential`モデルを使います。モデルを構築するステップは`build_model`という1つの関数の中に組み込みます。あとから2つ目のモデルを構築するためです。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "c26juK7ZG8j-" + }, + "outputs": [], + "source": [ + "def build_model():\n", + " model = keras.Sequential([\n", + " layers.Dense(64, activation='relu', input_shape=[len(train_dataset.keys())]),\n", + " layers.Dense(64, activation='relu'),\n", + " layers.Dense(1)\n", + " ])\n", + "\n", + " optimizer = tf.keras.optimizers.RMSprop(0.001)\n", + "\n", + " model.compile(loss='mse',\n", + " optimizer=optimizer,\n", + " metrics=['mae', 'mse'])\n", + " return model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "cGbPb-PHGbhs" + }, + "outputs": [], + "source": [ + "model = build_model()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Sj49Og4YGULr" + }, + "source": [ + "### モデルの検証\n", + "\n", + "`.summary`メソッドを使って、モデルの簡単な説明を表示します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "ReAD0n6MsFK-" + }, + "outputs": [], + "source": [ + "model.summary()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Vt6W50qGsJAL" + }, + "source": [ + "では、モデルを試してみましょう。訓練用データのうち`10`個のサンプルからなるバッチを取り出し、それを使って`model.predict`メソッドを呼び出します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "-d-gBaVtGTSC" + }, + "outputs": [], + "source": [ + "example_batch = normed_train_data[:10]\n", + "example_result = model.predict(example_batch)\n", + "example_result" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "QlM8KrSOsaYo" + }, + "source": [ + "うまく動作しているようです。予定通りの型と形状の出力が得られています。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "0-qWCsh6DlyH" + }, + "source": [ + "### モデルの訓練\n", + "\n", + "モデルを1000エポック訓練し、訓練と検証の正解率を`history`オブジェクトに記録します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "sD7qHCmNIOY0" + }, + "outputs": [], + "source": [ + "# エポックが終わるごとにドットを一つ出力することで進捗を表示\n", + "class PrintDot(keras.callbacks.Callback):\n", + " def on_epoch_end(self, epoch, logs):\n", + " if epoch % 100 == 0: print('')\n", + " print('.', end='')\n", + "\n", + "EPOCHS = 1000\n", + "\n", + "history = model.fit(\n", + " normed_train_data, train_labels,\n", + " epochs=EPOCHS, validation_split = 0.2, verbose=0,\n", + " callbacks=[PrintDot()])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "tQm3pc0FYPQB" + }, + "source": [ + "`history`オブジェクトに保存された数値を使ってモデルの訓練の様子を可視化します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "4Xj91b-dymEy" + }, + "outputs": [], + "source": [ + "hist = pd.DataFrame(history.history)\n", + "hist['epoch'] = history.epoch\n", + "hist.tail()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "B6XriGbVPh2t" + }, + "outputs": [], + "source": [ + "def plot_history(history):\n", + " hist = pd.DataFrame(history.history)\n", + " hist['epoch'] = history.epoch\n", + " \n", + " plt.figure()\n", + " plt.xlabel('Epoch')\n", + " plt.ylabel('Mean Abs Error [MPG]')\n", + " plt.plot(hist['epoch'], hist['mae'],\n", + " label='Train Error')\n", + " plt.plot(hist['epoch'], hist['val_mae'],\n", + " label = 'Val Error')\n", + " plt.ylim([0,5])\n", + " plt.legend()\n", + " \n", + " plt.figure()\n", + " plt.xlabel('Epoch')\n", + " plt.ylabel('Mean Square Error [$MPG^2$]')\n", + " plt.plot(hist['epoch'], hist['mse'],\n", + " label='Train Error')\n", + " plt.plot(hist['epoch'], hist['val_mse'],\n", + " label = 'Val Error')\n", + " plt.ylim([0,20])\n", + " plt.legend()\n", + " plt.show()\n", + "\n", + "\n", + "plot_history(history)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "AqsuANc11FYv" + }, + "source": [ + "このグラフを見ると、検証エラーは100エポックを過ぎたあたりで改善が見られなくなり、むしろ悪化しているようです。検証スコアの改善が見られなくなったら自動的に訓練を停止するように、`model.fit`メソッド呼び出しを変更します。ここでは、エポック毎に訓練状態をチェックする*EarlyStopping*コールバックを使用します。設定したエポック数の間に改善が見られない場合、訓練を自動的に停止します。\n", + "\n", + "このコールバックについての詳細は[ここ](https://www.tensorflow.org/versions/master/api_docs/python/tf/keras/callbacks/EarlyStopping)を参照ください。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "fdMZuhUgzMZ4" + }, + "outputs": [], + "source": [ + "model = build_model()\n", + "\n", + "# The patience parameter is the amount of epochs to check for improvement\n", + "early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=10)\n", + "\n", + "history = model.fit(normed_train_data, train_labels, epochs=EPOCHS,\n", + " validation_split = 0.2, verbose=0, callbacks=[early_stop, PrintDot()])\n", + "\n", + "plot_history(history)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "3St8-DmrX8P4" + }, + "source": [ + "検証用データセットでのグラフを見ると、平均誤差は+/- 2 MPG(マイル/ガロン)前後です。これは良い精度でしょうか?その判断はおまかせします。\n", + "\n", + "モデルの訓練に使用していない**テスト用**データセットを使って、モデルがどれくらい汎化できているか見てみましょう。これによって、モデルが実際の現場でどれくらい正確に予測できるかがわかります。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "jl_yNr5n1kms" + }, + "outputs": [], + "source": [ + "loss, mae, mse = model.evaluate(normed_test_data, test_labels, verbose=0)\n", + "\n", + "print(\"Testing set Mean Abs Error: {:5.2f} MPG\".format(mae))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "ft603OzXuEZC" + }, + "source": [ + "### モデルを使った予測\n", + "\n", + "最後に、テストデータを使ってMPG値を予測します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "Xe7RXH3N3CWU" + }, + "outputs": [], + "source": [ + "test_predictions = model.predict(normed_test_data).flatten()\n", + "\n", + "plt.scatter(test_labels, test_predictions)\n", + "plt.xlabel('True Values [MPG]')\n", + "plt.ylabel('Predictions [MPG]')\n", + "plt.axis('equal')\n", + "plt.axis('square')\n", + "plt.xlim([0,plt.xlim()[1]])\n", + "plt.ylim([0,plt.ylim()[1]])\n", + "_ = plt.plot([-100, 100], [-100, 100])\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "OrkHGKZcusUo" + }, + "source": [ + "そこそこ良い予測ができているように見えます。誤差の分布を見てみましょう。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "f-OHX4DiXd8x" + }, + "outputs": [], + "source": [ + "error = test_predictions - test_labels\n", + "plt.hist(error, bins = 25)\n", + "plt.xlabel(\"Prediction Error [MPG]\")\n", + "_ = plt.ylabel(\"Count\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "r9_kI6MHu1UU" + }, + "source": [ + "とても正規分布には見えませんが、サンプル数が非常に小さいからだと考えられます。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "vgGQuV-yqYZH" + }, + "source": [ + "## 結論\n", + "\n", + "このノートブックでは、回帰問題を扱うためのテクニックをいくつか紹介しました。\n", + "\n", + "* 平均二乗誤差(MSE: Mean Squared Error)は回帰問題に使われる一般的な損失関数です(分類問題には異なる損失関数が使われます)。\n", + "* 同様に、回帰問題に使われる評価指標も分類問題とは異なります。回帰問題の一般的な評価指標は平均絶対誤差(MAE: Mean Absolute Error)です。\n", + "* 入力数値特徴量の範囲が異なっている場合、特徴量毎に同じ範囲に正規化するべきです。\n", + "* 訓練用データが多くない場合、過学習を避けるために少ない隠れ層を持つ小さいネットワークを使うというのが良い方策の1つです。\n", + "* Early Stoppingは過学習を防止するための便利な手法の一つです。" + ] + } + ], + "metadata": { + "colab": { + "collapsed_sections": [], + "name": "basic_regression.ipynb", + "private_outputs": true, + "provenance": [], + "toc_visible": true, + "version": "0.3.2" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/site/ja/r2/tutorials/keras/basic_text_classification.ipynb b/site/ja/r2/tutorials/keras/basic_text_classification.ipynb new file mode 100644 index 00000000000..1c7b2dcff0e --- /dev/null +++ b/site/ja/r2/tutorials/keras/basic_text_classification.ipynb @@ -0,0 +1,726 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Ic4_occAAiAT" + }, + "source": [ + "##### Copyright 2018 The TensorFlow Authors." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "cellView": "form", + "colab": {}, + "colab_type": "code", + "id": "ioaprt5q5US7" + }, + "outputs": [], + "source": [ + "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "cellView": "form", + "colab": {}, + "colab_type": "code", + "id": "yCl0eTNH5RS3" + }, + "outputs": [], + "source": [ + "#@title MIT License\n", + "#\n", + "# Copyright (c) 2017 François Chollet\n", + "#\n", + "# Permission is hereby granted, free of charge, to any person obtaining a\n", + "# copy of this software and associated documentation files (the \"Software\"),\n", + "# to deal in the Software without restriction, including without limitation\n", + "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n", + "# and/or sell copies of the Software, and to permit persons to whom the\n", + "# Software is furnished to do so, subject to the following conditions:\n", + "#\n", + "# The above copyright notice and this permission notice shall be included in\n", + "# all copies or substantial portions of the Software.\n", + "#\n", + "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n", + "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n", + "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n", + "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n", + "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n", + "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n", + "# DEALINGS IN THE SOFTWARE." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "ItXfxkxvosLH" + }, + "source": [ + "# 映画レビューのテキスト分類" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "hKY4XMc9o8iB" + }, + "source": [ + "\n", + " \n", + " \n", + " \n", + "
\n", + " View on TensorFlow.org\n", + " \n", + " Run in Google Colab\n", + " \n", + " View source on GitHub\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Eg62Pmz3o83v" + }, + "source": [ + "ここでは、映画のレビューをそのテキストを使って**肯定的**か**否定的**かに分類します。これは、二値分類あるいは2クラス分類という問題の例であり、機械学習において重要でいろいろな応用が可能なものです。\n", + "\n", + "ここでは、[Internet Movie Database](https://www.imdb.com/)から抽出した50,000件の映画レビューを含む、 [IMDB dataset](https://www.tensorflow.org/api_docs/python/tf/keras/datasets/imdb) を使います。レビューは訓練用とテスト用に25,000件ずつに分割されています。訓練用とテスト用のデータは**均衡**しています。言い換えると、それぞれが同数の肯定的及び否定的なレビューを含んでいます。\n", + "\n", + "ここでは、TensorFlowを使ってモデルを構築・訓練するためのハイレベルなAPIである [tf.keras](https://www.tensorflow.org/guide/keras)を使用します。`tf.keras`を使ったもう少し高度なテキスト分類のチュートリアルについては、 [MLCC Text Classification Guide](https://developers.google.com/machine-learning/guides/text-classification/)を参照してください。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "2ew7HTbPpCJH" + }, + "outputs": [], + "source": [ + "from __future__ import absolute_import, division, print_function\n", + "\n", + "!pip install tensorflow==2.0.0-alpha0\n", + "import tensorflow as tf\n", + "from tensorflow import keras\n", + "\n", + "import numpy as np\n", + "\n", + "print(tf.__version__)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "iAsKG535pHep" + }, + "source": [ + "## IMDB datasetのダウンロード\n", + "\n", + "IMDBデータセットは、TensorFlowにパッケージ化されています。それは前処理済みのものであり、(単語の連なりである)レビューが、整数の配列に変換されています。そこでは整数が辞書中の特定の単語を表します。\n", + "\n", + "次のコードは、IMDBデータセットをあなたのパソコンにダウンロードします。(すでにダウンロードしていれば、キャッシュされたコピーを使用します)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "zXXx5Oc3pOmN" + }, + "outputs": [], + "source": [ + "imdb = keras.datasets.imdb\n", + "\n", + "(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "odr-KlzO-lkL" + }, + "source": [ + "`num_words=10000`という引数は、訓練データ中に出てくる単語のうち、最も頻繁に出現する10,000個を保持するためのものです。データサイズを管理可能にするため、稀にしか出現しない単語は破棄されます。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "l50X3GfjpU4r" + }, + "source": [ + "## データの観察\n", + "\n", + "データの形式を理解するために少し時間を割いてみましょう。このデータセットは前処理済みで、サンプルそれぞれが、映画レビューの中の単語を表す整数の配列になっています。ラベルはそれぞれ、0または1の整数値で、0が否定的レビュー、1が肯定的なレビューを示しています。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "y8qCnve_-lkO" + }, + "outputs": [], + "source": [ + "print(\"Training entries: {}, labels: {}\".format(len(train_data), len(train_labels)))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "RnKvHWW4-lkW" + }, + "source": [ + "レビューのテキストは複数の整数に変換されており、それぞれの整数が辞書の中の特定の単語を表します。最初のレビューがどのようなものか見てみましょう。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "QtTS4kpEpjbi" + }, + "outputs": [], + "source": [ + "print(train_data[0])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "hIE4l_72x7DP" + }, + "source": [ + "映画のレビューはそれぞれ長さが異なっていることでしょう。次のコードで、最初と2つ目のレビューの単語の数を見てみます。ニューラルネットワークへの入力は同じ長さでなければならないため、後ほどその問題を解決する必要があります。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "X-6Ii9Pfx6Nr" + }, + "outputs": [], + "source": [ + "len(train_data[0]), len(train_data[1])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "4wJg2FiYpuoX" + }, + "source": [ + "### 整数を単語に戻してみる\n", + "\n", + "整数をテキストに戻す方法を知っていると便利です。整数を文字列にマッピングする辞書オブジェクトを検索するためのヘルパー関数を定義します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "tr5s_1alpzop" + }, + "outputs": [], + "source": [ + "# 単語を整数にマッピングする辞書\n", + "word_index = imdb.get_word_index()\n", + "\n", + "# インデックスの最初の方は予約済み\n", + "word_index = {k:(v+3) for k,v in word_index.items()} \n", + "word_index[\"\"] = 0\n", + "word_index[\"\"] = 1\n", + "word_index[\"\"] = 2 # unknown\n", + "word_index[\"\"] = 3\n", + "\n", + "reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])\n", + "\n", + "def decode_review(text):\n", + " return ' '.join([reverse_word_index.get(i, '?') for i in text])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "U3CNRvEZVppl" + }, + "source": [ + "`decode_review`を使うと、最初のレビューのテキストを表示できます。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "s_OqxmH6-lkn" + }, + "outputs": [], + "source": [ + "decode_review(train_data[0])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "lFP_XKVRp4_S" + }, + "source": [ + "## データの準備\n", + "\n", + "レビュー(整数の配列)は、ニューラルネットワークに投入する前に、テンソルに変換する必要があります。これには2つの方法があります。\n", + "\n", + "* 配列をワンホット(one-hot)エンコーディングと同じように、単語の出現を表す0と1のベクトルに変換します。例えば、[3, 5]という配列は、インデックス3と5を除いてすべてゼロの10,000次元のベクトルになります。そして、これをネットワークの最初の層、すなわち、浮動小数点のベクトルデータを扱うことができるDense(全結合)層とします。ただし、これは単語数×レビュー数の行列が必要なメモリ集約的な方法です。\n", + "* もう一つの方法では、配列をパディングによって同じ長さに揃え、`サンプル数 * 長さの最大値`の形の整数テンソルにします。そして、この形式を扱うことができるEmbedding(埋め込み)層をネットワークの最初の層にします。\n", + "\n", + "このチュートリアルでは、後者を採用することにします。\n", + "\n", + "映画レビューは同じ長さでなければならないので、長さを標準化する [pad_sequences](https://www.tensorflow.org/versions/r1.10/api_docs/python/tf/keras/preprocessing/sequence/pad_sequences) 関数を使うことにします。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "2jQv-omsHurp" + }, + "outputs": [], + "source": [ + "train_data = keras.preprocessing.sequence.pad_sequences(train_data,\n", + " value=word_index[\"\"],\n", + " padding='post',\n", + " maxlen=256)\n", + "\n", + "test_data = keras.preprocessing.sequence.pad_sequences(test_data,\n", + " value=word_index[\"\"],\n", + " padding='post',\n", + " maxlen=256)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "VO5MBpyQdipD" + }, + "source": [ + "サンプルの長さを見てみましょう。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "USSSBnkE-lky" + }, + "outputs": [], + "source": [ + "len(train_data[0]), len(train_data[1])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "QJoxZGyfjT5V" + }, + "source": [ + "次に、パディング済みの最初のサンプルを確認します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "TG8X9cqi-lk9" + }, + "outputs": [], + "source": [ + "print(train_data[0])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "LLC02j2g-llC" + }, + "source": [ + "## モデルの構築\n", + "\n", + "ニューラルネットワークは、層を積み重ねることで構成されます。この際、2つの大きな決定が必要です。\n", + "\n", + "* モデルにいくつの**層**を設けるか?\n", + "* 層ごとに何個の**隠れユニット**を使用するか?\n", + "\n", + "この例では、入力データは単語インデックスの配列で構成されています。推定の対象となるラベルは、0または1です。この問題のためのモデルを構築しましょう。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "xpKOoWgu-llD" + }, + "outputs": [], + "source": [ + "# 入力の形式は映画レビューで使われている語彙数(10,000語)\n", + "vocab_size = 10000\n", + "\n", + "model = keras.Sequential()\n", + "model.add(keras.layers.Embedding(vocab_size, 16))\n", + "model.add(keras.layers.GlobalAveragePooling1D())\n", + "model.add(keras.layers.Dense(16, activation='relu'))\n", + "model.add(keras.layers.Dense(1, activation='sigmoid'))\n", + "\n", + "model.summary()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "6PbKQ6mucuKL" + }, + "source": [ + "これらの層は、分類器を構成するため一列に積み重ねられます。\n", + "\n", + "1. 最初の層は`Embedding`(埋め込み)層です。この層は、整数にエンコードされた語彙を受け取り、それぞれの単語インデックスに対応する埋め込みベクトルを検索します。埋め込みベクトルは、モデルの訓練の中で学習されます。ベクトル化のために、出力行列には次元が1つ追加されます。その結果、次元は、`(batch, sequence, embedding)`となります。\n", + "2. 次は、`GlobalAveragePooling1D`(1次元のグローバル平均プーリング)層です。この層は、それぞれのサンプルについて、シーケンスの次元方向に平均値をもとめ、固定長のベクトルを返します。この結果、モデルは最も単純な形で、可変長の入力を扱うことができるようになります。\n", + "3. この固定長の出力ベクトルは、16個の隠れユニットを持つ全結合(`Dense`)層に受け渡されます。\n", + "4. 最後の層は、1個の出力ノードに全結合されます。シグモイド(`sigmoid`)活性化関数を使うことで、値は確率あるいは確信度を表す0と1の間の浮動小数点数となります。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "0XMwnDOp-llH" + }, + "source": [ + "### 隠れユニット\n", + "\n", + "上記のモデルには、入力と出力の間に、2つの中間層あるいは「隠れ」層があります。出力(ユニット、ノード、またはニューロン)は、その層の内部表現の次元数です。言い換えると、このネットワークが学習によって内部表現を獲得する際の自由度ということです。\n", + "\n", + "モデルにより多くの隠れユニットがある場合(内部表現空間の次元数がより大きい場合)、または、より多くの層がある場合、あるいはその両方の場合、ネットワークはより複雑な内部表現を学習することができます。しかしながら、その結果として、ネットワークの計算量が多くなるほか、学習してほしくないパターンを学習するようになります。学習してほしくないパターンとは、訓練データでの性能は向上するものの、テスト用データの性能が向上しないパターンです。この問題を**過学習**(*overfitting*)といいます。この問題は後ほど検証することになります。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "L4EqVWg4-llM" + }, + "source": [ + "### 損失関数とオプティマイザ\n", + "\n", + "モデルを訓練するには、損失関数とオプティマイザが必要です。今回の問題は二値分類問題であり、モデルの出力は確率(1ユニットの層とシグモイド活性化関数)であるため、損失関数として`binary_crossentropy`(2値のクロスエントロピー)関数を使用することにします。\n", + "\n", + "損失関数の候補はこれだけではありません。例えば、`mean_squared_error`(平均二乗誤差)を使うこともできます。しかし、一般的には、確率を扱うには`binary_crossentropy`の方が適しています。`binary_crossentropy`は、確率分布の間の「距離」を測定する尺度です。今回の場合には、真の分布と予測値の分布の間の距離ということになります。\n", + "\n", + "後ほど、回帰問題を検証する際には(例えば家屋の値段を推定するとか)、もう一つの損失関数である`mean_squared_error`(平均二乗誤差)の使い方を目にすることになります。\n", + "\n", + "さて、モデルのオプティマイザと損失関数を設定しましょう。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "Mr0GP-cQ-llN" + }, + "outputs": [], + "source": [ + "model.compile(optimizer='adam',\n", + " loss='binary_crossentropy',\n", + " metrics=['accuracy'])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "hCWYwkug-llQ" + }, + "source": [ + "## 検証用データを作る\n", + "\n", + "訓練を行う際、モデルが見ていないデータでの正解率を検証したいと思います。もとの訓練用データから、10,000個のサンプルを取り分けて**検証用データ**(*validation set*)を作ります。(なぜ、ここでテスト用データを使わないのでしょう? 今回の目的は、訓練用データだけを使って、モデルの開発とチューニングを行うことです。その後、テスト用データを1回だけ使い、正解率を検証するのです。)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "-NpcXY9--llS" + }, + "outputs": [], + "source": [ + "x_val = train_data[:10000]\n", + "partial_x_train = train_data[10000:]\n", + "\n", + "y_val = train_labels[:10000]\n", + "partial_y_train = train_labels[10000:]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "35jv_fzP-llU" + }, + "source": [ + "## モデルの訓練\n", + "\n", + "512個のサンプルからなるミニバッチを使って、40エポックモデルを訓練します。この結果、`x_train`と`y_train`に含まれるすべてのサンプルを40回繰り返すことになります。訓練中、検証用データの10,000サンプルを用いて、モデルの損失と正解率をモニタリングします。 " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "tXSGrjWZ-llW", + "scrolled": false + }, + "outputs": [], + "source": [ + "history = model.fit(partial_x_train,\n", + " partial_y_train,\n", + " epochs=40,\n", + " batch_size=512,\n", + " validation_data=(x_val, y_val),\n", + " verbose=1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "9EEGuDVuzb5r" + }, + "source": [ + "## モデルの評価\n", + "\n", + "さて、モデルの性能を見てみましょう。2つの値が返されます。損失(エラーを示す数値であり、小さい方が良い)と正解率です。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "zOMKywn4zReN" + }, + "outputs": [], + "source": [ + "results = model.evaluate(test_data, test_labels)\n", + "\n", + "print(results)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "z1iEXVTR0Z2t" + }, + "source": [ + "この、かなり素朴なアプローチでも87%前後の正解率を達成しました。もっと高度なアプローチを使えば、モデルの正解率は95%に近づけることもできるでしょう。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "5KggXVeL-llZ" + }, + "source": [ + "## 正解率と損失の時系列グラフを描く\n", + "\n", + "`model.fit()` は、訓練中に発生したすべてのことを記録した辞書を含む`History` オブジェクトを返します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "VcvSXvhp-llb" + }, + "outputs": [], + "source": [ + "history_dict = history.history\n", + "history_dict.keys()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "nRKsqL40-lle" + }, + "source": [ + "4つのエントリがあります。それぞれが、訓練と検証の際にモニターしていた指標を示します。これを使って、訓練時と検証時の損失を比較するグラフと、訓練時と検証時の正解率を比較するグラフを作成することができます。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "nGoYf2Js-lle" + }, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "acc = history_dict['accuracy']\n", + "val_acc = history_dict['val_accuracy']\n", + "loss = history_dict['loss']\n", + "val_loss = history_dict['val_loss']\n", + "\n", + "epochs = range(1, len(acc) + 1)\n", + "\n", + "# \"bo\" is for \"blue dot\"\n", + "plt.plot(epochs, loss, 'bo', label='Training loss')\n", + "# b is for \"solid blue line\"\n", + "plt.plot(epochs, val_loss, 'b', label='Validation loss')\n", + "plt.title('Training and validation loss')\n", + "plt.xlabel('Epochs')\n", + "plt.ylabel('Loss')\n", + "plt.legend()\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "6hXx-xOv-llh" + }, + "outputs": [], + "source": [ + "plt.clf() # 図のクリア\n", + "\n", + "plt.plot(epochs, acc, 'bo', label='Training acc')\n", + "plt.plot(epochs, val_acc, 'b', label='Validation acc')\n", + "plt.title('Training and validation accuracy')\n", + "plt.xlabel('Epochs')\n", + "plt.ylabel('Accuracy')\n", + "plt.legend()\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "oFEmZ5zq-llk" + }, + "source": [ + "上記のグラフでは、点が訓練時の損失と正解率を、実線が検証時の損失と正解率を表しています。\n", + "\n", + "訓練時の損失がエポックごとに**減少**し、訓練時の正解率がエポックごとに**上昇**していることに気がつくはずです。繰り返すごとに指定された数値指標を最小化する勾配降下法を最適化に使用している場合に期待される動きです。\n", + "\n", + "これは、検証時の損失と正解率には当てはまりません。20エポックを過ぎたあたりから、横ばいになっているようです。これが、過学習の一例です。モデルの性能が、訓練用データでは高い一方で、見たことの無いデータではそれほど高くないというものです。このポイントをすぎると、モデルが最適化しすぎて、訓練用データでは特徴的であるが、テスト用データには一般化できない内部表現を学習しています。\n", + "\n", + "このケースの場合、20エポックを過ぎたあたりで訓練をやめることで、過学習を防止することが出来ます。後ほど、コールバックを使って、これを自動化する方法を紹介します。" + ] + } + ], + "metadata": { + "colab": { + "collapsed_sections": [], + "name": "basic_text_classification.ipynb", + "private_outputs": true, + "provenance": [], + "toc_visible": true, + "version": "0.3.2" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/site/ja/r2/tutorials/keras/feature_columns.ipynb b/site/ja/r2/tutorials/keras/feature_columns.ipynb new file mode 100644 index 00000000000..61b3d647539 --- /dev/null +++ b/site/ja/r2/tutorials/keras/feature_columns.ipynb @@ -0,0 +1,731 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "rNdWfPXCjTjY" + }, + "source": [ + "##### Copyright 2019 The TensorFlow Authors." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "cellView": "form", + "colab": {}, + "colab_type": "code", + "id": "I1dUQ0GejU8N" + }, + "outputs": [], + "source": [ + "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "c05P9g5WjizZ" + }, + "source": [ + "# 構造化されたデータの分類" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "zofH_gCzgplN" + }, + "source": [ + "\n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " View on TensorFlow.org\n", + " \n", + " \n", + " \n", + " Run in Google Colab\n", + " \n", + " \n", + " \n", + " View source on GitHub\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "K1y4OHpGgss7" + }, + "source": [ + "このチュートリアルでは、(例えばCSVファイルに保存された表形式データのような)構造化されたデータをどうやって分類するかを示します。ここでは、モデルの定義に[Keras](https://www.tensorflow.org/guide/keras)を、[feature columns](https://www.tensorflow.org/guide/feature_columns)をCSVファイルの列をモデルを訓練するための特徴量にマッピングするための橋渡し役として使用します。このチュートリアルには、下記のことを行うコードすべてが含まれています。\n", + "\n", + "* [Pandas](https://pandas.pydata.org/)を使用したCSVファイルの読み込み\n", + "* [tf.data](https://www.tensorflow.org/guide/datasets)を使用して行データをシャッフルし、バッチ化するための入力パイプライン構築\n", + "* feature columnsを使ったCSVの列のモデル訓練用の特徴量へのマッピング\n", + "* Kerasを使ったモデルの構築と、訓練及び評価\n", + "\n", + "## データセット\n", + "\n", + "ここでは、Cleveland Clinic Foundation for Heart Diseaseが提供している小さな[データセット](https://archive.ics.uci.edu/ml/datasets/heart+Disease)を使用します。このCSVファイルには数百行が含まれています。行が患者を、列がその属性を表します。この情報を使用して、患者が心臓疾患を持っているかを予測します。このデータセットの場合には二値分類タスクとなります。\n", + "\n", + "下記はこのデータセットの[說明](https://archive.ics.uci.edu/ml/machine-learning-databases/heart-disease/heart-disease.names)です。数値列とカテゴリー列があることに注目してください。\n", + "\n", + ">列| 說明| 特徴量の型 | データ型\n", + ">------------|--------------------|----------------------|-----------------\n", + ">Age | 年齢 | 数値型 | 整数\n", + ">Sex | (1 = 男性; 0 = 女性) | カテゴリー型 | 整数\n", + ">CP | 胸痛のタイプ (0, 1, 2, 3, 4) | カテゴリー型 | 整数\n", + ">Trestbpd | 安静時血圧 (単位:mm Hg 入院時) | 数値型 | 整数\n", + ">Chol | 血清コレステロール 単位:mg/dl | 数値型 | 整数\n", + ">FBS | (空腹時血糖 > 120 mg/dl) (1 = 真; 0 = 偽) | カテゴリー型 | 整数\n", + ">RestECG | 安静時心電図の診断結果 (0, 1, 2) | カテゴリー型 | 整数\n", + ">Thalach | 最大心拍数 | 数値型 | 整数\n", + ">Exang | 運動誘発狭心症 (1 = はい; 0 = いいえ) | カテゴリー型 | 整数\n", + ">Oldpeak | 安静時と比較した運動時のST低下 | 数値型 | 整数\n", + ">Slope | ピーク運動STセグメントの勾配 | 数値型 | 浮動小数点数\n", + ">CA | 蛍光透視法によって着色された主要血管の数(0−3) | 数値型 | 整数\n", + ">Thal | 3 = 正常; 6 = 固定欠陥; 7 = 可逆的欠陥 | カテゴリー型 | 文字列\n", + ">Target | 心臓疾患の診断 (1 = 真; 0 = 偽) | 分類 | 整数" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "VxyBFc_kKazA" + }, + "source": [ + "## TensorFlow他ライブラリのインポート" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "LuOWVJBz8a6G" + }, + "outputs": [], + "source": [ + "!pip install sklearn" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "9dEreb4QKizj" + }, + "outputs": [], + "source": [ + "from __future__ import absolute_import, division, print_function\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "!pip install tensorflow==2.0.0-alpha0\n", + "import tensorflow as tf\n", + "\n", + "from tensorflow import feature_column\n", + "from tensorflow.keras import layers\n", + "from sklearn.model_selection import train_test_split" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "KCEhSZcULZ9n" + }, + "source": [ + "## Pandasを使ったデータフレーム作成\n", + "\n", + "[Pandas](https://pandas.pydata.org/)は、構造化データの読み込みや操作のための便利なユーティリティを持つPythonのライブラリです。ここでは、Pandasを使ってURLからデータをダウンロードし、データフレームに読み込みます。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "REZ57BXCLdfG" + }, + "outputs": [], + "source": [ + "URL = 'https://storage.googleapis.com/applied-dl/heart.csv'\n", + "dataframe = pd.read_csv(URL)\n", + "dataframe.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "u0zhLtQqMPem" + }, + "source": [ + "## データフレームを、訓練用、検証用、テスト用に分割\n", + "\n", + "ダウンロードしたデータセットは1つのCSVファイルです。これを、訓練用、検証用、テスト用のデータセットに分割します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "YEOpw7LhMYsI" + }, + "outputs": [], + "source": [ + "train, test = train_test_split(dataframe, test_size=0.2)\n", + "train, val = train_test_split(train, test_size=0.2)\n", + "print(len(train), 'train examples')\n", + "print(len(val), 'validation examples')\n", + "print(len(test), 'test examples')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "84ef46LXMfvu" + }, + "source": [ + "## tf.dataを使った入力パイプラインの構築\n", + "\n", + "次に、[tf.data](https://www.tensorflow.org/guide/datasets)を使ってデータフレームをラップします。こうすることで、feature columns をPandasデータフレームの列をモデル訓練用の特徴量へのマッピングするための橋渡し役として使うことができます。(メモリに収まらないぐらいの)非常に大きなCSVファイルを扱う場合には、tf.dataを使ってディスクから直接CSVファイルを読み込むことになります。この方法は、このチュートリアルでは扱いません。" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "NkcaMYP-MsRe" + }, + "outputs": [], + "source": [ + "# Pandasデータフレームからtf.dataデータセットを作るためのユーティリティメソッド\n", + "def df_to_dataset(dataframe, shuffle=True, batch_size=32):\n", + " dataframe = dataframe.copy()\n", + " labels = dataframe.pop('target')\n", + " ds = tf.data.Dataset.from_tensor_slices((dict(dataframe), labels))\n", + " if shuffle:\n", + " ds = ds.shuffle(buffer_size=len(dataframe))\n", + " ds = ds.batch(batch_size)\n", + " return ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "CXbbXkJvMy34" + }, + "outputs": [], + "source": [ + "batch_size = 5 # デモ用として小さなバッチサイズを使用\n", + "train_ds = df_to_dataset(train, batch_size=batch_size)\n", + "val_ds = df_to_dataset(val, shuffle=False, batch_size=batch_size)\n", + "test_ds = df_to_dataset(test, shuffle=False, batch_size=batch_size)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "qRLGSMDzM-dl" + }, + "source": [ + "## 入力パイプラインを理解する\n", + "\n", + "入力パイプラインを構築したので、それが返すデータのフォーマットを見るために呼び出してみましょう。出力を読みやすくするためにバッチサイズを小さくしてあります。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "CSBo3dUVNFc9" + }, + "outputs": [], + "source": [ + "for feature_batch, label_batch in train_ds.take(1):\n", + " print('Every feature:', list(feature_batch.keys()))\n", + " print('A batch of ages:', feature_batch['age'])\n", + " print('A batch of targets:', label_batch )" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "OT5N6Se-NQsC" + }, + "source": [ + "データセットが(データフレームにある)列名からなるディクショナリを返すことがわかります。列名から、データフレームの行に含まれる列の値が得られます。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "ttIvgLRaNoOQ" + }, + "source": [ + "## feature columnsの様々な型の例\n", + "\n", + "TensorFlowにはたくさんの型のfeature columnがあります。このセクションでは、いくつかの型のfeature columnsを作り、データフレームの列をどのように変換しているかを示します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "mxwiHFHuNhmf" + }, + "outputs": [], + "source": [ + "# いくつかの型のfeature columnsを例示するためこのバッチを使用する\n", + "example_batch = next(iter(train_ds))[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "0wfLB8Q3N3UH" + }, + "outputs": [], + "source": [ + "# feature columnsを作りデータのバッチを変換する\n", + "# ユーティリティメソッド\n", + "def demo(feature_column):\n", + " feature_layer = layers.DenseFeatures(feature_column)\n", + " print(feature_layer(example_batch).numpy())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Q7OEKe82N-Qb" + }, + "source": [ + "### 数値コラム\n", + "\n", + "feature columnsの出力はモデルへの入力になります(上記で定義したdemo関数を使うと、データフレームの列がどのように変換されるかをつぶさに見ることができます)。[数値コラム](https://www.tensorflow.org/api_docs/python/tf/feature_column/numeric_column)は、最も単純な型のコラムです。数値コラムは実数特徴量を表現するのに使われます。このコラムを使う場合、モデルにはデータフレームの列の値がそのまま渡されます。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "QZTZ0HnHOCxC" + }, + "outputs": [], + "source": [ + "age = feature_column.numeric_column(\"age\")\n", + "demo(age)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "7a6ddSyzOKpq" + }, + "source": [ + "心臓疾患データセットでは、データフレームのほとんどの列が数値型です。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "IcSxUoYgOlA1" + }, + "source": [ + "### バケット化コラム\n", + "\n", + "数値をそのままモデルに入力するのではなく、値の範囲に基づいた異なるカテゴリーに分割したいことがあります。例えば、人の年齢を表す生データを考えてみましょう。[バケット化コラム](https://www.tensorflow.org/api_docs/python/tf/feature_column/bucketized_column)を使うと年齢を数値コラムとして表現するのではなく、年齢をいくつかのバケットに分割できます。下記のワンホット値が、各行がどの年齢範囲にあるかを表していることに注目してください。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "wJ4Wt3SAOpTQ" + }, + "outputs": [], + "source": [ + "age_buckets = feature_column.bucketized_column(age, boundaries=[18, 25, 30, 35, 40, 45, 50, 55, 60, 65])\n", + "demo(age_buckets)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "r1tArzewPb-b" + }, + "source": [ + "### カテゴリー型コラム\n", + "\n", + "このデータセットでは、Thalは('fixed'、'normal'、'reversible'のような)文字列として表現されています。文字列を直接モデルに入力することはできません。まず、文字列を数値にマッピングする必要があります。categorical vocabulary コラムを使うと、(上記で示した年齢バケットのように)文字列をワンホットベクトルとして表現することができます。カテゴリーを表す語彙(vocabulary)は[categorical_column_with_vocabulary_list](https://www.tensorflow.org/api_docs/python/tf/feature_column/categorical_column_with_vocabulary_list)を使ってリストで渡すか、[categorical_column_with_vocabulary_file](https://www.tensorflow.org/api_docs/python/tf/feature_column/categorical_column_with_vocabulary_file)を使ってファイルから読み込むことができます。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "DJ6QnSHkPtOC" + }, + "outputs": [], + "source": [ + "thal = feature_column.categorical_column_with_vocabulary_list(\n", + " 'thal', ['fixed', 'normal', 'reversible'])\n", + "\n", + "thal_one_hot = feature_column.indicator_column(thal)\n", + "demo(thal_one_hot)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "dxQloQ9jOoXL" + }, + "source": [ + "より複雑なデータセットでは、たくさんの列がカテゴリー型(例えば文字列)であることでしょう。feature columns はカテゴリー型データを扱う際に最も役に立ちます。このデータセットでは、カテゴリー型コラムは1つだけですが、他のデータセットを扱う際に使用できるいくつかの重要な型のfeature columnsを紹介するために、この列を使用することにします。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "LEFPjUr6QmwS" + }, + "source": [ + "### 埋め込み型コラム\n", + "\n", + "数種類の候補となる文字列ではなく、カテゴリー毎に数千(あるいはそれ以上)の値があるとしましょう。カテゴリーの数が多くなってくると、様々な理由から、ワンホットエンコーディングを使ってニューラルネットワークを訓練することが難しくなります。埋込み型コラムを使うと、こうした制約を克服することが可能です。[埋込み型コラム](https://www.tensorflow.org/api_docs/python/tf/feature_column/embedding_column)は、データを多次元のワンホットベクトルとして表すのではなく、セルの値が0か1かだけではなく、どんな数値でもとれるような密な低次元ベクトルとして表現します。埋め込みのサイズ(下記の例では8)は、チューニングが必要なパラメータです。\n", + "\n", + "キーポイント:カテゴリー型コラムがたくさんの選択肢を持つ場合、埋め込み型コラムを使用することが最善の方法です。ここでは例を一つ示しますので、今後様々なデータセットを扱う際には、この例を参考にしてください。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "hSlohmr2Q_UU" + }, + "outputs": [], + "source": [ + "# この埋込み型コラムの入力は、先程作成したカテゴリ型コラムであることに注意\n", + "thal_embedding = feature_column.embedding_column(thal, dimension=8)\n", + "demo(thal_embedding)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "urFCAvTVRMpB" + }, + "source": [ + "### ハッシュ化特徴コラム\n", + "\n", + "値の種類が多いカテゴリー型コラムを表現するもう一つの方法が、[categorical_column_with_hash_bucket](https://www.tensorflow.org/api_docs/python/tf/feature_column/categorical_column_with_hash_bucket)を使う方法です。このfeature columnは文字列をエンコードするために入力のハッシュ値を計算し、`hash_bucket_size`個のバケットの中から1つを選択します。このコラムを使用する場合には、語彙を用意する必要はありません。また、スペースの節約のために、実際のカテゴリー数に比べて極めて少ないバケット数を選択することも可能です。\n", + "\n", + "キーポイント:この手法の重要な欠点の一つは、異なる文字列が同じバケットにマッピングされるというハッシュ値の衝突が起きることです。実務上は、データセットによっては、この問題を無視できることがあります。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "YHU_Aj2nRRDC" + }, + "outputs": [], + "source": [ + "thal_hashed = feature_column.categorical_column_with_hash_bucket(\n", + " 'thal', hash_bucket_size=1000)\n", + "demo(feature_column.indicator_column(thal_hashed))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "fB94M27DRXtZ" + }, + "source": [ + "### クロスフィーチャーコラム\n", + "\n", + "複数の特徴量をまとめて1つの特徴量にする、[フィーチャークロス](https://developers.google.com/machine-learning/glossary/#feature_cross)として知られている手法は、モデルが特徴量の組み合わせの一つ一つに別々の重みを学習することを可能にします。ここでは年齢とThalをクロスさせて新しい特徴量を作ってみます。交差列(`crossed_column`)が、起こりうるすべての組み合わせ全体のテーブル(これは非常に大きくなる可能性があります)を作るものではないことに注意してください。クロスフィーチャーコラムは、代わりにバックエンドとしてハッシュ化コラムを使用しているため、テーブルの大きさを選択することができます。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "oaPVERd9Rep6" + }, + "outputs": [], + "source": [ + "crossed_feature = feature_column.crossed_column([age_buckets, thal], hash_bucket_size=1000)\n", + "demo(feature_column.indicator_column(crossed_feature))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "ypkI9zx6Rj1q" + }, + "source": [ + "## 使用するコラムを選択する\n", + "\n", + "これまで、いくつかのfeature columnの使い方を見てきました。いよいよモデルの訓練にそれらを使用することにします。このチュートリアルの目的は、feature columnsを使うのに必要な完全なコード(いわば力学)を示すことです。以下ではモデルを訓練するための列を適当に選びました。\n", + "\n", + "キーポイント:正確なモデルを構築するのが目的である場合には、できるだけ大きなデータセットを使用して、どの特徴量を含めるのがもっとも意味があるのかや、それらをどう表現したらよいかを、慎重に検討してください。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "4PlLY7fORuzA" + }, + "outputs": [], + "source": [ + "feature_columns = []\n", + "\n", + "# 数値コラム\n", + "for header in ['age', 'trestbps', 'chol', 'thalach', 'oldpeak', 'slope', 'ca']:\n", + " feature_columns.append(feature_column.numeric_column(header))\n", + "\n", + "# バケット化コラム\n", + "age_buckets = feature_column.bucketized_column(age, boundaries=[18, 25, 30, 35, 40, 45, 50, 55, 60, 65])\n", + "feature_columns.append(age_buckets)\n", + "\n", + "# インジケーター(カテゴリー型)コラム\n", + "thal = feature_column.categorical_column_with_vocabulary_list(\n", + " 'thal', ['fixed', 'normal', 'reversible'])\n", + "thal_one_hot = feature_column.indicator_column(thal)\n", + "feature_columns.append(thal_one_hot)\n", + "\n", + "# 埋め込み型コラム\n", + "thal_embedding = feature_column.embedding_column(thal, dimension=8)\n", + "feature_columns.append(thal_embedding)\n", + "\n", + "# クロスフィーチャーコラム\n", + "crossed_feature = feature_column.crossed_column([age_buckets, thal], hash_bucket_size=1000)\n", + "crossed_feature = feature_column.indicator_column(crossed_feature)\n", + "feature_columns.append(crossed_feature)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "M-nDp8krS_ts" + }, + "source": [ + "### 特徴量層の構築\n", + "\n", + "feature columnsを定義し終わったので、次に[DenseFeatures](https://www.tensorflow.org/versions/r2.0/api_docs/python/tf/keras/layers/DenseFeatures)層を使ってKerasモデルへの入力とします。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "6o-El1R2TGQP" + }, + "outputs": [], + "source": [ + "feature_layer = tf.keras.layers.DenseFeatures(feature_columns)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "8cf6vKfgTH0U" + }, + "source": [ + "これまでは、feature columnsの働きを見るため、小さなバッチサイズを使ってきました。ここではもう少し大きなバッチサイズの新しい入力パイプラインを作ります。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "gcemszoGSse_" + }, + "outputs": [], + "source": [ + "batch_size = 32\n", + "train_ds = df_to_dataset(train, batch_size=batch_size)\n", + "val_ds = df_to_dataset(val, shuffle=False, batch_size=batch_size)\n", + "test_ds = df_to_dataset(test, shuffle=False, batch_size=batch_size)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "bBx4Xu0eTXWq" + }, + "source": [ + "## モデルの構築、コンパイルと訓練" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "_YJPPb3xTPeZ" + }, + "outputs": [], + "source": [ + "model = tf.keras.Sequential([\n", + " feature_layer,\n", + " layers.Dense(128, activation='relu'),\n", + " layers.Dense(128, activation='relu'),\n", + " layers.Dense(1, activation='sigmoid')\n", + "])\n", + "\n", + "model.compile(optimizer='adam',\n", + " loss='binary_crossentropy',\n", + " metrics=['accuracy'])\n", + "\n", + "model.fit(train_ds, \n", + " validation_data=val_ds, \n", + " epochs=5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "GnFmMOW0Tcaa" + }, + "outputs": [], + "source": [ + "loss, accuracy = model.evaluate(test_ds)\n", + "print(\"Accuracy\", accuracy)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "3bdfbq20V6zu" + }, + "source": [ + "キーポイント:一般的に、ディープラーニングが最良の結果となるのは、もっと大きくて、もっと複雑なデータセットです。この例のように小さなデータセットを使用する際には、強固なベースラインとして、決定木やランダムフォレストを使うことをおすすめします。このチュートリアルの目的は、訓練により正確なモデルを得ることではなく、構造化データの使い方をデモすることです。今後ご自分のデータセットに取り組まれる際の出発点として、これらのコードをお使いください。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "SotnhVWuHQCw" + }, + "source": [ + "## 次のステップ\n", + "\n", + "構造化データの分類について更に多くのことを学ぶためには、自分自身で試してみることです。別のデータセットを見つけ、上記と同様のコードを使って、それを分類するモデルを訓練してみてください。正解率を上げるためには、モデルにどの特徴量を含めたらよいかや、その特徴量をどのように表現すべきかをじっくり考えてください。" + ] + } + ], + "metadata": { + "colab": { + "collapsed_sections": [], + "name": "feature_columns.ipynb", + "private_outputs": true, + "provenance": [], + "toc_visible": true, + "version": "0.3.2" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/site/ja/r2/tutorials/keras/index.md b/site/ja/r2/tutorials/keras/index.md new file mode 100644 index 00000000000..baa4fbb86a5 --- /dev/null +++ b/site/ja/r2/tutorials/keras/index.md @@ -0,0 +1,22 @@ +# 機械学習の学習と実践 + +この一連のノートブックは、書籍 *[Deep Learning with Python](https://books.google.com/books?id=Yo3CAQAACAAJ)* +(邦訳「[PythonとKerasによるディープラーニング](https://book.mynavi.jp/ec/products/detail/id=90124) 」)に触発された +ものです。 +これらのチュートリアルでは、TensorFlowでディープラーニングモデルの +構築と訓練を行うためのハイレベルなPython APIである`tf.keras`を +使用しています。KerasをTensorFlowとともに使う方法の詳細は、 +[TensorFlow Keras Guide](../../guide/keras)を参照してください。 + +出版社から:*Deep Learning with Python*では、Python言語と強力なKeras +ライブラリを使ってディープラーニングを紹介しています。 +著者はKerasの作者でGoogleのAI研究者でもあるFrançois Cholletです。 +この本では、直感的な説明と実践的な例を通して理解を深めることができます。 + +機械学習の基礎と概念を学ぶには、[Machine Learning Crash Course](https://developers.google.com/machine-learning/crash-course/)をおすすめします。TensorFlowと機械学習については[next steps](../next_steps)に更に掲載されています。 + +1. [分類問題の基本](./basic_classification.ipynb) +2. [テキスト分類](./basic_text_classification.ipynb) +3. [回帰](./basic_regression.ipynb) +4. [過学習と学習不足](./overfit_and_underfit.ipynb) +5. [モデルの保存と復元](./save_and_restore_models.ipynb) diff --git a/site/ja/r2/tutorials/keras/overfit_and_underfit.ipynb b/site/ja/r2/tutorials/keras/overfit_and_underfit.ipynb new file mode 100644 index 00000000000..ea3560f4367 --- /dev/null +++ b/site/ja/r2/tutorials/keras/overfit_and_underfit.ipynb @@ -0,0 +1,724 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "fTFj8ft5dlbS" + }, + "source": [ + "##### Copyright 2018 The TensorFlow Authors." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "cellView": "form", + "colab": {}, + "colab_type": "code", + "id": "lzyBOpYMdp3F" + }, + "outputs": [], + "source": [ + "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "cellView": "form", + "colab": {}, + "colab_type": "code", + "id": "m_x4KfSJ7Vt7" + }, + "outputs": [], + "source": [ + "#@title MIT License\n", + "#\n", + "# Copyright (c) 2017 François Chollet\n", + "#\n", + "# Permission is hereby granted, free of charge, to any person obtaining a\n", + "# copy of this software and associated documentation files (the \"Software\"),\n", + "# to deal in the Software without restriction, including without limitation\n", + "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n", + "# and/or sell copies of the Software, and to permit persons to whom the\n", + "# Software is furnished to do so, subject to the following conditions:\n", + "#\n", + "# The above copyright notice and this permission notice shall be included in\n", + "# all copies or substantial portions of the Software.\n", + "#\n", + "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n", + "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n", + "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n", + "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n", + "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n", + "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n", + "# DEALINGS IN THE SOFTWARE." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "C9HmC2T4ld5B" + }, + "source": [ + "# 過学習と学習不足について知る" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "kRTxFhXAlnl1" + }, + "source": [ + "\n", + " \n", + " \n", + " \n", + "
\n", + " View on TensorFlow.org\n", + " \n", + " Run in Google Colab\n", + " \n", + " View source on GitHub\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "19rPukKZsPG6" + }, + "source": [ + "いつものように、この例のプログラムは`tf.keras` APIを使用します。詳しくはTensorFlowの[Keras guide](https://www.tensorflow.org/guide/keras)を参照してください。\n", + "\n", + "これまでの例、つまり、映画レビューの分類と燃費の推定では、検証用データでのモデルの正解率が、数エポックでピークを迎え、その後低下するという現象が見られました。\n", + "\n", + "言い換えると、モデルが訓練用データを**過学習**したと考えられます。過学習への対処の仕方を学ぶことは重要です。**訓練用データセット**で高い正解率を達成することは難しくありませんが、我々は、(これまで見たこともない)**テスト用データ**に汎化したモデルを開発したいのです。\n", + "\n", + "過学習の反対語は**学習不足**(underfitting)です。学習不足は、モデルがテストデータに対してまだ改善の余地がある場合に発生します。学習不足の原因は様々です。モデルが十分強力でないとか、正則化のしすぎだとか、単に訓練時間が短すぎるといった理由があります。学習不足は、訓練用データの中の関連したパターンを学習しきっていないということを意味します。\n", + "\n", + "モデルの訓練をやりすぎると、モデルは過学習を始め、訓練用データの中のパターンで、テストデータには一般的ではないパターンを学習します。我々は、過学習と学習不足の中間を目指す必要があります。これから見ていくように、ちょうどよいエポック数だけ訓練を行うというのは必要なスキルなのです。\n", + "\n", + "過学習を防止するための、最良の解決策は、より多くの訓練用データを使うことです。多くのデータで訓練を行えば行うほど、モデルは自然により汎化していく様になります。これが不可能な場合、次善の策は正則化のようなテクニックを使うことです。正則化は、モデルに保存される情報の量とタイプに制約を課すものです。ネットワークが少数のパターンしか記憶できなければ、最適化プロセスにより、最も主要なパターンのみを学習することになり、より汎化される可能性が高くなります。\n", + "\n", + "このノートブックでは、重みの正則化とドロップアウトという、よく使われる2つの正則化テクニックをご紹介します。これらを使って、IMDBの映画レビューを分類するノートブックの改善を図ります。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "5pZ8A2liqvgk" + }, + "outputs": [], + "source": [ + "from __future__ import absolute_import, division, print_function\n", + "\n", + "!pip install tensorflow==2.0.0-alpha0\n", + "import tensorflow as tf\n", + "from tensorflow import keras\n", + "\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "\n", + "print(tf.__version__)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "1cweoTiruj8O" + }, + "source": [ + "## IMDBデータセットのダウンロード\n", + "\n", + "以前のノートブックで使用したエンベディングの代わりに、ここでは文をマルチホットエンコードします。このモデルは、訓練用データセットをすぐに過学習します。このモデルを使って、過学習がいつ起きるかということと、どうやって過学習と戦うかをデモします。\n", + "\n", + "リストをマルチホットエンコードすると言うのは、0と1のベクトルにするということです。具体的にいうと、例えば`[3, 5]`というシーケンスを、インデックス3と5の値が1で、それ以外がすべて0の、10,000次元のベクトルに変換するということを意味します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "QpzE4iqZtJly" + }, + "outputs": [], + "source": [ + "NUM_WORDS = 10000\n", + "\n", + "(train_data, train_labels), (test_data, test_labels) = keras.datasets.imdb.load_data(num_words=NUM_WORDS)\n", + "\n", + "def multi_hot_sequences(sequences, dimension):\n", + " # 形状が (len(sequences), dimension)ですべて0の行列を作る\n", + " results = np.zeros((len(sequences), dimension))\n", + " for i, word_indices in enumerate(sequences):\n", + " results[i, word_indices] = 1.0 # 特定のインデックスに対してresults[i] を1に設定する\n", + " return results\n", + "\n", + "\n", + "train_data = multi_hot_sequences(train_data, dimension=NUM_WORDS)\n", + "test_data = multi_hot_sequences(test_data, dimension=NUM_WORDS)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "MzWVeXe3NBTn" + }, + "source": [ + "結果として得られるマルチホットベクトルの1つを見てみましょう。単語のインデックスは頻度順にソートされています。このため、インデックスが0に近いほど1が多く出現するはずです。分布を見てみましょう。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "71kr5rG4LkGM" + }, + "outputs": [], + "source": [ + "plt.plot(train_data[0])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "lglk41MwvU5o" + }, + "source": [ + "## 過学習のデモ\n", + "\n", + "過学習を防止するための最も単純な方法は、モデルのサイズ、すなわち、モデル内の学習可能なパラメータの数を小さくすることです(学習パラメータの数は、層の数と層ごとのユニット数で決まります)。ディープラーニングでは、モデルの学習可能なパラメータ数を、しばしばモデルの「キャパシティ」と呼びます。直感的に考えれば、パラメータ数の多いモデルほど「記憶容量」が大きくなり、訓練用のサンプルとその目的変数の間の辞書のようなマッピングをたやすく学習することができます。このマッピングには汎化能力がまったくなく、これまで見たことが無いデータを使って予測をする際には役に立ちません。\n", + "\n", + "ディープラーニングのモデルは訓練用データに適応しやすいけれど、本当のチャレレンジは汎化であって適応ではないということを、肝に銘じておく必要があります。\n", + "\n", + "一方、ネットワークの記憶容量が限られている場合、前述のようなマッピングを簡単に学習することはできません。損失を減らすためには、より予測能力が高い圧縮された表現を学習しなければなりません。同時に、モデルを小さくしすぎると、訓練用データに適応するのが難しくなります。「多すぎる容量」と「容量不足」の間にちょうどよい容量があるのです。\n", + "\n", + "残念ながら、(層の数や、層ごとの大きさといった)モデルの適切なサイズやアーキテクチャを決める魔法の方程式はありません。一連の異なるアーキテクチャを使って実験を行う必要があります。\n", + "\n", + "適切なモデルのサイズを見つけるには、比較的少ない層の数とパラメータから始めるのがベストです。それから、検証用データでの損失値の改善が見られなくなるまで、徐々に層の大きさを増やしたり、新たな層を加えたりします。映画レビューの分類ネットワークでこれを試してみましょう。\n", + "\n", + "比較基準として、```Dense```層だけを使ったシンプルなモデルを構築し、その後、それより小さいバージョンと大きいバージョンを作って比較します。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "_ReKHdC2EgVu" + }, + "source": [ + "### 比較基準を作る" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "QKgdXPx9usBa" + }, + "outputs": [], + "source": [ + "baseline_model = keras.Sequential([\n", + " # `.summary` を見るために`input_shape`が必要 \n", + " keras.layers.Dense(16, activation='relu', input_shape=(NUM_WORDS,)),\n", + " keras.layers.Dense(16, activation='relu'),\n", + " keras.layers.Dense(1, activation='sigmoid')\n", + "])\n", + "\n", + "baseline_model.compile(optimizer='adam',\n", + " loss='binary_crossentropy',\n", + " metrics=['accuracy', 'binary_crossentropy'])\n", + "\n", + "baseline_model.summary()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "LqG3MXF5xSjR" + }, + "outputs": [], + "source": [ + "baseline_history = baseline_model.fit(train_data,\n", + " train_labels,\n", + " epochs=20,\n", + " batch_size=512,\n", + " validation_data=(test_data, test_labels),\n", + " verbose=2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "L-DGRBbGxI6G" + }, + "source": [ + "### より小さいモデルの構築" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "SrfoVQheYSO5" + }, + "source": [ + "今作成したばかりの比較基準となるモデルに比べて隠れユニット数が少ないモデルを作りましょう。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "jksi-XtaxDAh" + }, + "outputs": [], + "source": [ + "smaller_model = keras.Sequential([\n", + " keras.layers.Dense(4, activation='relu', input_shape=(NUM_WORDS,)),\n", + " keras.layers.Dense(4, activation='relu'),\n", + " keras.layers.Dense(1, activation='sigmoid')\n", + "])\n", + "\n", + "smaller_model.compile(optimizer='adam',\n", + " loss='binary_crossentropy',\n", + " metrics=['accuracy', 'binary_crossentropy'])\n", + "\n", + "smaller_model.summary()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "jbngCZliYdma" + }, + "source": [ + "同じデータを使って訓練します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "Ofn1AwDhx-Fe" + }, + "outputs": [], + "source": [ + "smaller_history = smaller_model.fit(train_data,\n", + " train_labels,\n", + " epochs=20,\n", + " batch_size=512,\n", + " validation_data=(test_data, test_labels),\n", + " verbose=2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "vIPuf23FFaVn" + }, + "source": [ + "### より大きなモデルの構築\n", + "\n", + "練習として、より大きなモデルを作成し、どれほど急速に過学習が起きるかを見ることもできます。次はこのベンチマークに、この問題が必要とするよりはるかに容量の大きなネットワークを追加しましょう。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "ghQwwqwqvQM9" + }, + "outputs": [], + "source": [ + "bigger_model = keras.models.Sequential([\n", + " keras.layers.Dense(512, activation='relu', input_shape=(NUM_WORDS,)),\n", + " keras.layers.Dense(512, activation='relu'),\n", + " keras.layers.Dense(1, activation='sigmoid')\n", + "])\n", + "\n", + "bigger_model.compile(optimizer='adam',\n", + " loss='binary_crossentropy',\n", + " metrics=['accuracy','binary_crossentropy'])\n", + "\n", + "bigger_model.summary()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "D-d-i5DaYmr7" + }, + "source": [ + "このモデルもまた同じデータを使って訓練します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "U1A99dhqvepf" + }, + "outputs": [], + "source": [ + "bigger_history = bigger_model.fit(train_data, train_labels,\n", + " epochs=20,\n", + " batch_size=512,\n", + " validation_data=(test_data, test_labels),\n", + " verbose=2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Fy3CMUZpzH3d" + }, + "source": [ + "### 訓練時と検証時の損失をグラフにする\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "HSlo1F4xHuuM" + }, + "source": [ + "実線は訓練用データセットの損失、破線は検証用データセットでの損失です(検証用データでの損失が小さい方が良いモデルです)。これをみると、小さいネットワークのほうが比較基準のモデルよりも過学習が始まるのが遅いことがわかります(4エポックではなく6エポック後)。また、過学習が始まっても性能の低下がよりゆっくりしています。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "0XmKDtOWzOpk" + }, + "outputs": [], + "source": [ + "def plot_history(histories, key='binary_crossentropy'):\n", + " plt.figure(figsize=(16,10))\n", + " \n", + " for name, history in histories:\n", + " val = plt.plot(history.epoch, history.history['val_'+key],\n", + " '--', label=name.title()+' Val')\n", + " plt.plot(history.epoch, history.history[key], color=val[0].get_color(),\n", + " label=name.title()+' Train')\n", + "\n", + " plt.xlabel('Epochs')\n", + " plt.ylabel(key.replace('_',' ').title())\n", + " plt.legend()\n", + "\n", + " plt.xlim([0,max(history.epoch)])\n", + "\n", + "\n", + "plot_history([('baseline', baseline_history),\n", + " ('smaller', smaller_history),\n", + " ('bigger', bigger_history)])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Bi6hBhdnSfjA" + }, + "source": [ + "より大きなネットワークでは、すぐに、1エポックで過学習が始まり、その度合も強いことに注目してください。ネットワークの容量が大きいほど訓練用データをモデル化するスピードが早くなり(結果として訓練時の損失値が小さくなり)ますが、より過学習しやすく(結果として訓練時の損失値と検証時の損失値が大きく乖離しやすく)なります。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "ASdv7nsgEFhx" + }, + "source": [ + "## 過学習防止の戦略" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "4rHoVWcswFLa" + }, + "source": [ + "### 重みの正則化を加える\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "kRxWepNawbBK" + }, + "source": [ + "「オッカムの剃刀」の原則をご存知でしょうか。何かの説明が2つあるとすると、最も正しいと考えられる説明は、仮定の数が最も少ない「一番単純な」説明だというものです。この原則は、ニューラルネットワークを使って学習されたモデルにも当てはまります。ある訓練用データとネットワーク構造があって、そのデータを説明できる重みの集合が複数ある時(つまり、複数のモデルがある時)、単純なモデルのほうが複雑なものよりも過学習しにくいのです。\n", + "\n", + "ここで言う「単純なモデル」とは、パラメータ値の分布のエントロピーが小さいもの(あるいは、上記で見たように、そもそもパラメータの数が少ないもの)です。したがって、過学習を緩和するための一般的な手法は、重みが小さい値のみをとることで、重み値の分布がより整然となる(正則)様に制約を与えるものです。これを「重みの正則化」と呼ばれ、ネットワークの損失関数に、重みの大きさに関連するコストを加えることで行われます。このコストには2つの種類があります。\n", + "\n", + "* [L1正則化](https://developers.google.com/machine-learning/glossary/#L1_regularization) 重み係数の絶対値に比例するコストを加える(重みの「L1ノルム」と呼ばれる)。\n", + "\n", + "* [L2正則化](https://developers.google.com/machine-learning/glossary/#L2_regularization) 重み係数の二乗に比例するコストを加える(重み係数の二乗「L2ノルム」と呼ばれる)。L2正則化はニューラルネットワーク用語では重み減衰(Weight Decay)と呼ばれる。呼び方が違うので混乱しないように。重み減衰は数学的にはL2正則化と同義である。\n", + "\n", + "L1正則化は重みパラメータの一部を0にすることでモデルを疎にする効果があります。L2正則化は重みパラメータにペナルティを加えますがモデルを疎にすることはありません。これは、L2正則化のほうが一般的である理由の一つです。\n", + "\n", + "`tf.keras`では、重みの正則化をするために、重み正則化のインスタンスをキーワード引数として層に加えます。ここでは、L2正則化を追加してみましょう。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "HFGmcwduwVyQ" + }, + "outputs": [], + "source": [ + "l2_model = keras.models.Sequential([\n", + " keras.layers.Dense(16, kernel_regularizer=keras.regularizers.l2(0.001),\n", + " activation='relu', input_shape=(NUM_WORDS,)),\n", + " keras.layers.Dense(16, kernel_regularizer=keras.regularizers.l2(0.001),\n", + " activation='relu'),\n", + " keras.layers.Dense(1, activation='sigmoid')\n", + "])\n", + "\n", + "l2_model.compile(optimizer='adam',\n", + " loss='binary_crossentropy',\n", + " metrics=['accuracy', 'binary_crossentropy'])\n", + "\n", + "l2_model_history = l2_model.fit(train_data, train_labels,\n", + " epochs=20,\n", + " batch_size=512,\n", + " validation_data=(test_data, test_labels),\n", + " verbose=2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "bUUHoXb7w-_C" + }, + "source": [ + "```l2(0.001)```というのは、層の重み行列の係数全てに対して```0.001 * 重み係数の値 **2```をネットワークの損失値合計に加えることを意味します。このペナルティは訓練時のみに加えられるため、このネットワークの損失値は、訓練時にはテスト時に比べて大きくなることに注意してください。\n", + "\n", + "L2正則化の影響を見てみましょう。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "7wkfLyxBZdh_" + }, + "outputs": [], + "source": [ + "plot_history([('baseline', baseline_history),\n", + " ('l2', l2_model_history)])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Kx1YHMsVxWjP" + }, + "source": [ + "ご覧のように、L2正則化ありのモデルは比較基準のモデルに比べて過学習しにくくなっています。両方のモデルのパラメータ数は同じであるにもかかわらずです。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "HmnBNOOVxiG8" + }, + "source": [ + "### ドロップアウトを追加する\n", + "\n", + "ドロップアウトは、ニューラルネットワークの正則化テクニックとして最もよく使われる手法の一つです。この手法は、トロント大学のヒントンと彼の学生が開発したものです。ドロップアウトは層に適用するもので、訓練時に層から出力された特徴量に対してランダムに「ドロップアウト(つまりゼロ化)」を行うものです。例えば、ある層が訓練時にある入力サンプルに対して、普通は`[0.2, 0.5, 1.3, 0.8, 1.1]` というベクトルを出力するとします。ドロップアウトを適用すると、このベクトルは例えば`[0, 0.5, 1.3, 0, 1.1]`のようにランダムに散らばったいくつかのゼロを含むようになります。「ドロップアウト率」はゼロ化される特徴の割合で、通常は0.2から0.5の間に設定します。テスト時は、どのユニットもドロップアウトされず、代わりに出力値がドロップアウト率と同じ比率でスケールダウンされます。これは、訓練時に比べてたくさんのユニットがアクティブであることに対してバランスをとるためです。\n", + "\n", + "`tf.keras`では、Dropout層を使ってドロップアウトをネットワークに導入できます。ドロップアウト層は、その直前の層の出力に対してドロップアウトを適用します。\n", + "\n", + "それでは、IMDBネットワークに2つのドロップアウト層を追加しましょう。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "OFEYvtrHxSWS" + }, + "outputs": [], + "source": [ + "dpt_model = keras.models.Sequential([\n", + " keras.layers.Dense(16, activation='relu', input_shape=(NUM_WORDS,)),\n", + " keras.layers.Dropout(0.5),\n", + " keras.layers.Dense(16, activation='relu'),\n", + " keras.layers.Dropout(0.5),\n", + " keras.layers.Dense(1, activation='sigmoid')\n", + "])\n", + "\n", + "dpt_model.compile(optimizer='adam',\n", + " loss='binary_crossentropy',\n", + " metrics=['accuracy','binary_crossentropy'])\n", + "\n", + "dpt_model_history = dpt_model.fit(train_data, train_labels,\n", + " epochs=20,\n", + " batch_size=512,\n", + " validation_data=(test_data, test_labels),\n", + " verbose=2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "SPZqwVchx5xp" + }, + "outputs": [], + "source": [ + "plot_history([('baseline', baseline_history),\n", + " ('dropout', dpt_model_history)])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "QioQ5e9GN0bM" + }, + "source": [ + "ドロップアウトを追加することで、比較対象モデルより明らかに改善が見られます。\n", + "\n", + "まとめ:ニューラルネットワークにおいて過学習を防ぐ最も一般的な方法は次のとおりです。\n", + "\n", + "* 訓練データを増やす\n", + "* ネットワークの容量をへらす\n", + "* 重みの正則化を行う\n", + "* ドロップアウトを追加する\n", + "\n", + "このガイドで触れていない2つの重要なアプローチがあります。データ拡張とバッチ正規化です。" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "collapsed_sections": [ + "fTFj8ft5dlbS" + ], + "name": "overfit-and-underfit.ipynb のコピー", + "private_outputs": true, + "provenance": [], + "toc_visible": true, + "version": "0.3.2" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.5" + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/site/ja/r2/tutorials/keras/save_and_restore_models.ipynb b/site/ja/r2/tutorials/keras/save_and_restore_models.ipynb new file mode 100644 index 00000000000..4e14c7e69d3 --- /dev/null +++ b/site/ja/r2/tutorials/keras/save_and_restore_models.ipynb @@ -0,0 +1,876 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "g_nWetWWd_ns" + }, + "source": [ + "##### Copyright 2018 The TensorFlow Authors." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "cellView": "form", + "colab": {}, + "colab_type": "code", + "id": "2pHVBk_seED1" + }, + "outputs": [], + "source": [ + "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "cellView": "form", + "colab": {}, + "colab_type": "code", + "id": "N_fMsQ-N8I7j" + }, + "outputs": [], + "source": [ + "#@title MIT License\n", + "#\n", + "# Copyright (c) 2017 François Chollet\n", + "#\n", + "# Permission is hereby granted, free of charge, to any person obtaining a\n", + "# copy of this software and associated documentation files (the \"Software\"),\n", + "# to deal in the Software without restriction, including without limitation\n", + "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n", + "# and/or sell copies of the Software, and to permit persons to whom the\n", + "# Software is furnished to do so, subject to the following conditions:\n", + "#\n", + "# The above copyright notice and this permission notice shall be included in\n", + "# all copies or substantial portions of the Software.\n", + "#\n", + "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n", + "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n", + "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n", + "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n", + "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n", + "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n", + "# DEALINGS IN THE SOFTWARE." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "pZJ3uY9O17VN" + }, + "source": [ + "# モデルの保存と復元" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "M4Ata7_wMul1" + }, + "source": [ + "\n", + " \n", + " \n", + " \n", + "
\n", + " View on TensorFlow.org\n", + " \n", + " Run in Google Colab\n", + " \n", + " View source on GitHub\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "mBdde4YJeJKF" + }, + "source": [ + "モデルは訓練中にも、訓練が終わったあとも保存できます。このことは、長い訓練時間を掛けなくても、やめたところから再開できるということを意味します。モデルが保存可能であることは、あなたが作ったモデルを他の人と共有できるということでもあります。研究結果であるモデルや手法を公開する際、機械学習の実務家はほとんど次のものを共有します。\n", + "\n", + "* モデルを構築するプログラム\n", + "* 学習済みモデルの重みあるいはパラメータ\n", + "\n", + "このデータを共有することで、他の人がモデルだどの様に動作するかを理解したり、新しいデータに試してみたりすることが容易になります。\n", + "\n", + "注意:信頼できないプログラムには気をつけましょう。TensorFlowのモデルもプログラムです。詳しくは、[Using TensorFlow Securely](https://github.com/tensorflow/tensorflow/blob/master/SECURITY.md)を参照してください。\n", + "\n", + "### オプション\n", + "\n", + "TensorFlowのモデルを保存する方法は、使っているAPIによって異なります。このガイドはTensorFlowのモデルを構築し訓練するためのハイレベルなAPIである[tf.keras](https://www.tensorflow.org/guide/keras)を使っています。この他のアプローチについては、TensorFlowの [Save and Restore](https://www.tensorflow.org/guide/saved_model) ガイド、あるいは、[Saving in eager](https://www.tensorflow.org/guide/eager#object-based_saving)を参照してください。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "xCUREq7WXgvg" + }, + "source": [ + "## 設定\n", + "\n", + "### インストールとインポート" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "7l0MiTOrXtNv" + }, + "source": [ + "TensorFlowと依存関係のライブラリをインストールし、インポートします。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "RzIOVSdnMYyO" + }, + "outputs": [], + "source": [ + "!pip install h5py pyyaml " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "SbGsznErXWt6" + }, + "source": [ + "### サンプルデータセットの取得\n", + "\n", + "ここでは、モデルを訓練し重みの保存をデモするために、 [MNIST dataset](http://yann.lecun.com/exdb/mnist/) を使います。デモの実行を速くするため、最初の1,000件のサンプルだけを使います。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "7Nm7Tyb-gRt-" + }, + "outputs": [], + "source": [ + "from __future__ import absolute_import, division, print_function\n", + "\n", + "import os\n", + "\n", + "!pip install tensorflow==2.0.0-alpha0\n", + "import tensorflow as tf\n", + "from tensorflow import keras\n", + "\n", + "tf.__version__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "9rGfFwE9XVwz" + }, + "outputs": [], + "source": [ + "(train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.mnist.load_data()\n", + "\n", + "train_labels = train_labels[:1000]\n", + "test_labels = test_labels[:1000]\n", + "\n", + "train_images = train_images[:1000].reshape(-1, 28 * 28) / 255.0\n", + "test_images = test_images[:1000].reshape(-1, 28 * 28) / 255.0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "anG3iVoXyZGI" + }, + "source": [ + "### モデルの定義" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "wynsOBfby0Pa" + }, + "source": [ + "重みの保存と読み込みのデモを行うための簡単なモデルを定義しましょう。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "0HZbJIjxyX1S" + }, + "outputs": [], + "source": [ + "# 短いシーケンシャルモデルを返す関数\n", + "def create_model():\n", + " model = tf.keras.models.Sequential([\n", + " keras.layers.Dense(512, activation='relu', input_shape=(784,)),\n", + " keras.layers.Dropout(0.2),\n", + " keras.layers.Dense(10, activation='softmax')\n", + " ])\n", + " \n", + " model.compile(optimizer='adam', \n", + " loss='sparse_categorical_crossentropy',\n", + " metrics=['accuracy'])\n", + " \n", + " return model\n", + "\n", + "\n", + "# 基本的なモデルのインスタンスを作る\n", + "model = create_model()\n", + "model.summary()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "soDE0W_KH8rG" + }, + "source": [ + "## 訓練中にチェックポイントを保存する" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "mRyd5qQQIXZm" + }, + "source": [ + "主な用途は訓練の**途中**あるいは**終了後**にチェックポイントを自動的に保存することです。こうすることにより、再び訓練を行うことなくモデルを使用することができ、また、訓練が中断された場合に、中止したところから再開できます。\n", + "\n", + "`tf.keras.callbacks.ModelCheckpoint`がこれを行うためのコールバックです。このコールバックにはチェックポイントを構成するためのいくつかの引数があります。\n", + "\n", + "### チェックポイントコールバックの使い方\n", + "\n", + "モデルの訓練時に、`ModelCheckpoint`を渡します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "IFPuhwntH8VH" + }, + "outputs": [], + "source": [ + "checkpoint_path = \"training_1/cp.ckpt\"\n", + "checkpoint_dir = os.path.dirname(checkpoint_path)\n", + "\n", + "# チェックポイントコールバックを作る\n", + "cp_callback = tf.keras.callbacks.ModelCheckpoint(checkpoint_path, \n", + " save_weights_only=True,\n", + " verbose=1)\n", + "\n", + "model = create_model()\n", + "\n", + "model.fit(train_images, train_labels, epochs = 10, \n", + " validation_data = (test_images,test_labels),\n", + " callbacks = [cp_callback]) # 訓練にコールバックを渡す\n", + "\n", + "# オプティマイザの状態保存についての警告が表示されるかもしれません。\n", + "# これらの警告は(このノートブックで発生する同様な警告を含めて)\n", + "# 古い用法を非推奨にするためのもので、無視して構いません。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "rlM-sgyJO084" + }, + "source": [ + "この結果、エポックごとに更新される一連のTensorFlowチェックポイントファイルが作成されます。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "gXG5FVKFOVQ3" + }, + "outputs": [], + "source": [ + "!ls {checkpoint_dir}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "wlRN_f56Pqa9" + }, + "source": [ + "訓練していない新しいモデルを作ります。重みだけからモデルを復元する場合には、元のモデルと同じアーキテクチャのモデルが必要です。モデルのアーキテクチャが同じであるため、モデルの異なる**インスタンス**であっても重みを共有することができるのです。\n", + "\n", + "訓練していない全く新しいモデルを作り、テストデータセットで評価します。訓練をしていないモデルは偶然のレベル(正解率10%以下)の性能しか無いはずです。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "Fp5gbuiaPqCT" + }, + "outputs": [], + "source": [ + "model = create_model()\n", + "\n", + "loss, acc = model.evaluate(test_images, test_labels)\n", + "print(\"Untrained model, accuracy: {:5.2f}%\".format(100*acc))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "1DTKpZssRSo3" + }, + "source": [ + "次に、チェックポイントから重みをロードし、再び評価します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "2IZxbwiRRSD2" + }, + "outputs": [], + "source": [ + "model.load_weights(checkpoint_path)\n", + "loss,acc = model.evaluate(test_images, test_labels)\n", + "print(\"Restored model, accuracy: {:5.2f}%\".format(100*acc))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "bpAbKkAyVPV8" + }, + "source": [ + "### チェックポイントコールバックのオプション\n", + "\n", + "このコールバックには、チェックポイントに一意な名前をつけたり、チェックポイントの頻度を調整するためのオプションがあります。\n", + "\n", + "新しいモデルを訓練し、5エポックごとに一意な名前のチェックポイントを保存します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "mQF_dlgIVOvq" + }, + "outputs": [], + "source": [ + "# ファイル名に(`str.format`を使って)エポック数を埋め込みます\n", + "checkpoint_path = \"training_2/cp-{epoch:04d}.ckpt\"\n", + "checkpoint_dir = os.path.dirname(checkpoint_path)\n", + "\n", + "cp_callback = tf.keras.callbacks.ModelCheckpoint(\n", + " checkpoint_path, verbose=1, save_weights_only=True,\n", + " # 重みを5エポックごとに保存します\n", + " period=5)\n", + "\n", + "model = create_model()\n", + "model.fit(train_images, train_labels,\n", + " epochs = 50, callbacks = [cp_callback],\n", + " validation_data = (test_images,test_labels),\n", + " verbose=0)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "1zFrKTjjavWI" + }, + "source": [ + "次に、出来上がったチェックポイントを確認し、最後のものを選択します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "p64q3-V4sXt0" + }, + "outputs": [], + "source": [ + "! ls {checkpoint_dir}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "1AN_fnuyR41H" + }, + "outputs": [], + "source": [ + "latest = tf.train.latest_checkpoint(checkpoint_dir)\n", + "latest" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Zk2ciGbKg561" + }, + "source": [ + "注意:デフォルトのtensorflowフォーマットは、直近の5つのチェックポイントのみを保存します。\n", + "\n", + "テストのため、モデルをリセットし最後のチェックポイントをロードします。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "3M04jyK-H3QK" + }, + "outputs": [], + "source": [ + "model = create_model()\n", + "model.load_weights(latest)\n", + "loss, acc = model.evaluate(test_images, test_labels)\n", + "print(\"Restored model, accuracy: {:5.2f}%\".format(100*acc))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "c2OxsJOTHxia" + }, + "source": [ + "## これらのファイルは何?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "JtdYhvWnH2ib" + }, + "source": [ + "上記のコードでは、重みだけをバイナリで[checkpoint](https://www.tensorflow.org/guide/saved_model#save_and_restore_variables)形式の一連のファイルに保存します。チェックポイントには、次のものが含まれます。\n", + "\n", + "* 1つ以上のモデルの重みの断片\n", + "* どの重みがどの断片に保存されているかを示すインデックスファイル\n", + "\n", + "1台のマシンだけでモデルの訓練を行っている場合には、`.data-00000-of-00001`のようなサフィックスのついたファイルが1つだけ作成されます。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "S_FA-ZvxuXQV" + }, + "source": [ + "## 手動で重みを保存する\n", + "\n", + "上記では重みをモデルにロードする方法を見ました。\n", + "\n", + "手動で重みを保存するのも同じ様に簡単です。`Model.save_weights` メソッドを使います。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "R7W5plyZ-u9X" + }, + "outputs": [], + "source": [ + "# 重みの保存\n", + "model.save_weights('./checkpoints/my_checkpoint')\n", + "\n", + "# 重みの復元\n", + "model = create_model()\n", + "model.load_weights('./checkpoints/my_checkpoint')\n", + "\n", + "loss,acc = model.evaluate(test_images, test_labels)\n", + "print(\"Restored model, accuracy: {:5.2f}%\".format(100*acc))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "kOGlxPRBEvV1" + }, + "source": [ + "## モデル全体の保存\n", + "\n", + "モデルとオプティマイザを、その状態(重みと変数)とモデルの設定の両方を含む1つのファイルに保存することができます。これにより、モデルをオリジナルのPythonコードにアクセスしなくとも使用できるようにエクスポートできます。オプティマイザの状態が復元されるので、中断したところから訓練を再開することも可能です。\n", + "\n", + "完全に機能するモデルを保存できるのは便利です。保存したモデルをTensorFlow.js ([HDF5](https://js.tensorflow.org/tutorials/import-keras.html), [Saved Model](https://js.tensorflow.org/tutorials/import-saved-model.html))でロードし、ブラウザで訓練したり、実行したりすることができるほか、TensorFlow Lite ([HDF5](https://www.tensorflow.org/lite/convert/python_api#exporting_a_tfkeras_file_), [Saved Model](https://www.tensorflow.org/lite/convert/python_api#exporting_a_savedmodel_))\n", + "を使ってモバイルデバイスで実行できるように変換することも可能です。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "SkGwf-50zLNn" + }, + "source": [ + "### HDF5ファイルとして\n", + "\n", + "Kerasでは、[HDF5](https://en.wikipedia.org/wiki/Hierarchical_Data_Format) 標準を使った基本的なファイルフォーマットが利用できます。ここでの利用目的では、保存されたモデルは単独のバイナリラージオブジェクト(blob)として扱うことができます。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "m2dkmJVCGUia" + }, + "outputs": [], + "source": [ + "model = create_model()\n", + "\n", + "model.fit(train_images, train_labels, epochs=5)\n", + "\n", + "# モデル全体を1つのHDF5ファイルに保存します。\n", + "model.save('my_model.h5')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "GWmttMOqS68S" + }, + "source": [ + "保存したファイルを使ってモデルを再作成します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "5NDMO_7kS6Do" + }, + "outputs": [], + "source": [ + "# 重みとオプティマイザを含む全く同じモデルを再作成\n", + "new_model = keras.models.load_model('my_model.h5')\n", + "new_model.summary()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "JXQpbTicTBwt" + }, + "source": [ + "正解率を検査します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "jwEaj9DnTCVA" + }, + "outputs": [], + "source": [ + "loss, acc = new_model.evaluate(test_images, test_labels)\n", + "print(\"Restored model, accuracy: {:5.2f}%\".format(100*acc))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "dGXqd4wWJl8O" + }, + "source": [ + "この方法では、次のすべてが保存されます。\n", + "\n", + "* 重みの値\n", + "* モデルの設定(アーキテクチャ)\n", + "* オプティマイザの設定\n", + "\n", + "Kerasは保存する際にアーキテクチャを調べます。いまのところ、TensorFlowのオプティマイザ(`tf.train`に含まれるもの)を保存することはできません。TensorFlowのオプティマイザを使用している場合には、モデルをロードしたあと再コンパイルする必要があり、オプティマイザの状態は失われます。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "kPyhgcoVzqUB" + }, + "source": [ + "### `saved_model`として" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "LtcN4VIb7JkK" + }, + "source": [ + "注意:この手法による`tf.keras`モデルの保存は実験的なもので、将来のバージョンで変更される可能性があります。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "DSWiSB0Q8c46" + }, + "source": [ + "新しいモデルを作ります。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "sI1YvCDFzpl3" + }, + "outputs": [], + "source": [ + "model = create_model()\n", + "\n", + "model.fit(train_images, train_labels, epochs=5)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "iUvT_3qE8hV5" + }, + "source": [ + "`saved_model`を作成し、タイムスタンプ付きのディレクトリに保存します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "sq8fPglI1RWA", + "scrolled": true + }, + "outputs": [], + "source": [ + "import time\n", + "saved_model_path = \"./saved_models/{}\".format(int(time.time()))\n", + "\n", + "tf.keras.experimental.export_saved_model(model, saved_model_path)\n", + "saved_model_path" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "MjpmyPfh8-1n" + }, + "source": [ + "作成したsaved_modelsを一覧表示します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "ZtOvxA7V0iTv" + }, + "outputs": [], + "source": [ + "!ls saved_models/" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "B7qfpvpY9HCe" + }, + "source": [ + "保存されたモデル(SavedModel)から新しいKerasモデルをリロードします。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "0YofwHdN0pxa" + }, + "outputs": [], + "source": [ + "new_model = tf.keras.experimental.load_from_saved_model(saved_model_path)\n", + "new_model.summary()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "uWwgNaz19TH2" + }, + "source": [ + "復元されたモデルを実行します。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model.predict(test_images).shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "Pc9e6G6w1AWG", + "scrolled": true + }, + "outputs": [], + "source": [ + "# モデルを評価する前にコンパイルする必要があります。\n", + "# モデルをデプロイするだけであればこのステップは不要です。\n", + "\n", + "new_model.compile(optimizer=model.optimizer, # ロードしてあったオプティマイザを保持\n", + " loss='sparse_categorical_crossentropy',\n", + " metrics=['accuracy'])\n", + "\n", + "# モデルを評価します。\n", + "loss, acc = new_model.evaluate(test_images, test_labels)\n", + "print(\"Restored model, accuracy: {:5.2f}%\".format(100*acc))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "eUYTzSz5VxL2" + }, + "source": [ + "## この先は?\n", + "\n", + "`tf.keras`を使った保存とロードのクイックガイドでした。\n", + "\n", + "* [tf.keras guide](https://www.tensorflow.org/guide/keras) には`tf.keras`での保存とロードについて、もう少し記載されています\n", + "\n", + "* Eager Executionでの保存については[Saving in eager](https://www.tensorflow.org/guide/eager#object_based_saving) を参照ください\n", + "\n", + "* [Save and Restore](https://www.tensorflow.org/guide/saved_model)ガイドには、TensorFlowでの保存についてローレベルの詳細が記載されています" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "collapsed_sections": [], + "name": "save_and_restore_models.ipynb", + "private_outputs": true, + "provenance": [], + "toc_visible": true, + "version": "0.3.2" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From f70a60d82ea496e004d8f3737037081d64b395f3 Mon Sep 17 00:00:00 2001 From: Billy Lamberta Date: Tue, 7 May 2019 10:51:39 -0700 Subject: [PATCH 2/2] Add community translation disclaimer. Colab formmating --- .../keras/basic_classification.ipynb | 2050 +++++++++-------- .../r2/tutorials/keras/basic_regression.ipynb | 1740 +++++++------- .../keras/basic_text_classification.ipynb | 1460 ++++++------ .../r2/tutorials/keras/feature_columns.ipynb | 1470 ++++++------ site/ja/r2/tutorials/keras/index.md | 23 +- .../keras/overfit_and_underfit.ipynb | 1425 ++++++------ .../keras/save_and_restore_models.ipynb | 1764 +++++++------- 7 files changed, 4984 insertions(+), 4948 deletions(-) diff --git a/site/ja/r2/tutorials/keras/basic_classification.ipynb b/site/ja/r2/tutorials/keras/basic_classification.ipynb index a74d52d2094..fa9783182ac 100644 --- a/site/ja/r2/tutorials/keras/basic_classification.ipynb +++ b/site/ja/r2/tutorials/keras/basic_classification.ipynb @@ -1,1021 +1,1031 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "MhoQ0WE77laV" - }, - "source": [ - "##### Copyright 2018 The TensorFlow Authors." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "cellView": "form", - "colab": {}, - "colab_type": "code", - "id": "_ckMIh7O7s6D" - }, - "outputs": [], - "source": [ - "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", - "# you may not use this file except in compliance with the License.\n", - "# You may obtain a copy of the License at\n", - "#\n", - "# https://www.apache.org/licenses/LICENSE-2.0\n", - "#\n", - "# Unless required by applicable law or agreed to in writing, software\n", - "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", - "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", - "# See the License for the specific language governing permissions and\n", - "# limitations under the License." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "cellView": "form", - "colab": {}, - "colab_type": "code", - "id": "vasWnqRgy1H4" - }, - "outputs": [], - "source": [ - "#@title MIT License\n", - "#\n", - "# Copyright (c) 2017 François Chollet\n", - "#\n", - "# Permission is hereby granted, free of charge, to any person obtaining a\n", - "# copy of this software and associated documentation files (the \"Software\"),\n", - "# to deal in the Software without restriction, including without limitation\n", - "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n", - "# and/or sell copies of the Software, and to permit persons to whom the\n", - "# Software is furnished to do so, subject to the following conditions:\n", - "#\n", - "# The above copyright notice and this permission notice shall be included in\n", - "# all copies or substantial portions of the Software.\n", - "#\n", - "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n", - "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n", - "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n", - "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n", - "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n", - "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n", - "# DEALINGS IN THE SOFTWARE." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "jYysdyb-CaWM" - }, - "source": [ - "# はじめてのニューラルネットワーク:分類問題の初歩" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "S5Uhzt6vVIB2" - }, - "source": [ - "\n", - " \n", - " \n", - " \n", - "
\n", - " View on TensorFlow.org\n", - " \n", - " Run in Google Colab\n", - " \n", - " View source on GitHub\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "FbVhjPpzn6BM" - }, - "source": [ - "このガイドでは、スニーカーやシャツなど、身に着けるものの写真を分類するニューラルネットワークのモデルを訓練します。すべての詳細を理解できなくても問題ありません。TensorFlowの全体を早足で掴むためのもので、詳細についてはあとから見ていくことになります。\n", - "\n", - "このガイドでは、TensorFlowのモデルを構築し訓練するためのハイレベルのAPIである [tf.keras](https://www.tensorflow.org/guide/keras)を使用します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "jL3OqFKZ9dFg" - }, - "outputs": [], - "source": [ - "!pip install tensorflow==2.0.0-alpha0" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "dzLKpmZICaWN" - }, - "outputs": [], - "source": [ - "from __future__ import absolute_import, division, print_function\n", - "\n", - "# TensorFlow と tf.keras のインポート\n", - "import tensorflow as tf\n", - "from tensorflow import keras\n", - "\n", - "# ヘルパーライブラリのインポート\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "\n", - "print(tf.__version__)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "yR0EdgrLCaWR" - }, - "source": [ - "## ファッションMNISTデータセットのロード" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "DLdCchMdCaWQ" - }, - "source": [ - "このガイドでは、[Fashion MNIST](https://github.com/zalandoresearch/fashion-mnist)を使用します。Fashion MNISTには10カテゴリーの白黒画像70,000枚が含まれています。それぞれは下図のような1枚に付き1種類の衣料品が写っている低解像度(28×28ピクセル)の画像です。\n", - "\n", - "\n", - " \n", - " \n", - "
\n", - " \"Fashion\n", - "
\n", - " Figure 1. Fashion-MNIST samples (by Zalando, MIT License).
 \n", - "
\n", - "\n", - "Fashion MNISTは、画像処理のための機械学習での\"Hello, World\"としてしばしば登場する[MNIST](http://yann.lecun.com/exdb/mnist/) データセットの代替として開発されたものです。MNISTデータセットは手書きの数字(0, 1, 2 など)から構成されており、そのフォーマットはこれから使うFashion MNISTと全く同じです。\n", - "\n", - "Fashion MNISTを使うのは、目先を変える意味もありますが、普通のMNISTよりも少しだけ手応えがあるからでもあります。どちらのデータセットも比較的小さく、アルゴリズムが期待したとおりに機能するかどうかを確かめるために使われます。プログラムのテストやデバッグのためには、よい出発点になります。\n", - "\n", - "ここでは、60,000枚の画像を訓練に、10,000枚の画像を、ネットワークが学習した画像分類の正確性を評価するのに使います。TensorFlowを使うと、下記のようにFashion MNISTのデータを簡単にインポートし、ロードすることが出来ます。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "7MqDQO0KCaWS" - }, - "outputs": [], - "source": [ - "fashion_mnist = keras.datasets.fashion_mnist\n", - "\n", - "(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "t9FDsUlxCaWW" - }, - "source": [ - "ロードしたデータセットは、NumPy配列になります。\n", - "\n", - "* `train_images` と `train_labels` の2つの配列は、モデルの訓練に使用される**訓練用データセット**です。\n", - "* 訓練されたモデルは、 `test_images` と `test_labels` 配列からなる**テスト用データセット**を使ってテストします。\n", - "\n", - "画像は28×28のNumPy配列から構成されています。それぞれのピクセルの値は0から255の間の整数です。**ラベル**(label)は、0から9までの整数の配列です。それぞれの数字が下表のように、衣料品の**クラス**(class)に対応しています。\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
LabelClass
0T-shirt/top
1Trouser
2Pullover
3Dress
4Coat
5Sandal
6Shirt
7Sneaker
8Bag
9Ankle boot
\n", - "\n", - "画像はそれぞれ単一のラベルに分類されます。データセットには上記の**クラス名**が含まれていないため、後ほど画像を出力するときのために、クラス名を保存しておきます。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "IjnLH5S2CaWx" - }, - "outputs": [], - "source": [ - "class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', \n", - " 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "Brm0b_KACaWX" - }, - "source": [ - "## データの観察\n", - "\n", - "モデルの訓練を行う前に、データセットのフォーマットを見てみましょう。下記のように、訓練用データセットには28×28ピクセルの画像が60,000枚含まれています。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "zW5k_xz1CaWX" - }, - "outputs": [], - "source": [ - "train_images.shape" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "cIAcvQqMCaWf" - }, - "source": [ - "同様に、訓練用データセットには60,000個のラベルが含まれます。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "TRFYHB2mCaWb" - }, - "outputs": [], - "source": [ - "len(train_labels)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "YSlYxFuRCaWk" - }, - "source": [ - "ラベルはそれぞれ、0から9までの間の整数です。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "XKnCTHz4CaWg" - }, - "outputs": [], - "source": [ - "train_labels" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "TMPI88iZpO2T" - }, - "source": [ - "テスト用データセットには、10,000枚の画像が含まれます。画像は28×28ピクセルで構成されています。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "2KFnYlcwCaWl" - }, - "outputs": [], - "source": [ - "test_images.shape" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "rd0A0Iu0CaWq" - }, - "source": [ - "テスト用データセットには10,000個のラベルが含まれます。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "iJmPr5-ACaWn" - }, - "outputs": [], - "source": [ - "len(test_labels)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "ES6uQoLKCaWr" - }, - "source": [ - "## データの前処理\n", - "\n", - "ネットワークを訓練する前に、データを前処理する必要があります。最初の画像を調べてみればわかるように、ピクセルの値は0から255の間の数値です。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "m4VEw8Ud9Quh" - }, - "outputs": [], - "source": [ - "plt.figure()\n", - "plt.imshow(train_images[0])\n", - "plt.colorbar()\n", - "plt.grid(False)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "3jCZdQNNCaWv" - }, - "source": [ - "ニューラルネットワークにデータを投入する前に、これらの値を0から1までの範囲にスケールします。そのためには、画素の値を255で割ります。\n", - "\n", - "**訓練用データセット**と**テスト用データセット**は、同じように前処理することが重要です。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "bW5WzIPlCaWv" - }, - "outputs": [], - "source": [ - "train_images = train_images / 255.0\n", - "\n", - "test_images = test_images / 255.0" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "Ee638AlnCaWz" - }, - "source": [ - "**訓練用データセット**の最初の25枚の画像を、クラス名付きで表示してみましょう。ネットワークを構築・訓練する前に、データが正しいフォーマットになっていることを確認します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "oZTImqg_CaW1" - }, - "outputs": [], - "source": [ - "plt.figure(figsize=(10,10))\n", - "for i in range(25):\n", - " plt.subplot(5,5,i+1)\n", - " plt.xticks([])\n", - " plt.yticks([])\n", - " plt.grid(False)\n", - " plt.imshow(train_images[i], cmap=plt.cm.binary)\n", - " plt.xlabel(class_names[train_labels[i]])\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "59veuiEZCaW4" - }, - "source": [ - "## モデルの構築\n", - "\n", - "ニューラルネットワークを構築するには、まずモデルの階層を定義し、その後モデルをコンパイルします。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "Gxg1XGm0eOBy" - }, - "source": [ - "### 層の設定\n", - "\n", - "ニューラルネットワークを形作る基本的な構成要素は**層**(layer)です。層は、入力されたデータから「表現」を抽出します。それらの「表現」は、今取り組もうとしている問題に対して、より「意味のある」ものであることが期待されます。\n", - "\n", - "ディープラーニングモデルのほとんどは、単純な層の積み重ねで構成されています。`tf.keras.layers.Dense` のような層のほとんどには、訓練中に学習されるパラメータが存在します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "9ODch-OFCaW4" - }, - "outputs": [], - "source": [ - "model = keras.Sequential([\n", - " keras.layers.Flatten(input_shape=(28, 28)),\n", - " keras.layers.Dense(128, activation='relu'),\n", - " keras.layers.Dense(10, activation='softmax')\n", - "])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "gut8A_7rCaW6" - }, - "source": [ - "このネットワークの最初の層は、`tf.keras.layers.Flatten` です。この層は、画像を(28×28ピクセルの)2次元配列から、28×28=784ピクセルの、1次元配列に変換します。この層が、画像の中に積まれているピクセルの行を取り崩し、横に並べると考えてください。この層には学習すべきパラメータはなく、ただデータのフォーマット変換を行うだけです。\n", - "\n", - "ピクセルが1次元化されたあと、ネットワークは2つの `tf.keras.layers.Dense` 層となります。これらの層は、密結合あるいは全結合されたニューロンの層となります。最初の `Dense` 層には、128個のノード(あるはニューロン)があります。最後の層でもある2番めの層は、10ノードの**softmax**層です。この層は、合計が1になる10個の確率の配列を返します。それぞれのノードは、今見ている画像が10個のクラスのひとつひとつに属する確率を出力します。\n", - "\n", - "### モデルのコンパイル\n", - "\n", - "モデルが訓練できるようになるには、いくつかの設定を追加する必要があります。それらの設定は、モデルの**コンパイル**(compile)時に追加されます。\n", - "\n", - "* **損失関数**(loss function) —訓練中にモデルがどれくらい正確かを測定します。この関数の値を最小化することにより、訓練中のモデルを正しい方向に向かわせようというわけです。\n", - "* **オプティマイザ**(optimizer)—モデルが見ているデータと、損失関数の値から、どのようにモデルを更新するかを決定します。\n", - "* **メトリクス**(metrics) —訓練とテストのステップを監視するのに使用します。下記の例では*accuracy* (正解率)、つまり、画像が正しく分類された比率を使用しています。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "Lhan11blCaW7" - }, - "outputs": [], - "source": [ - "model.compile(optimizer='adam', \n", - " loss='sparse_categorical_crossentropy',\n", - " metrics=['accuracy'])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "qKF6uW-BCaW-" - }, - "source": [ - "## モデルの訓練\n", - "\n", - "ニューラルネットワークの訓練には次のようなステップが必要です。\n", - "\n", - "1. モデルに訓練用データを投入します—この例では `train_images` と `train_labels` の2つの配列です。\n", - "2. モデルは、画像とラベルの対応関係を学習します。\n", - "3. モデルにテスト用データセットの予測(分類)を行わせます—この例では `test_images` 配列です。その後、予測結果と `test_labels` 配列を照合します。 \n", - "\n", - "訓練を開始するには、`model.fit` メソッドを呼び出します。モデルを訓練用データに \"fit\"(適合)させるという意味です。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "xvwvpA64CaW_" - }, - "outputs": [], - "source": [ - "model.fit(train_images, train_labels, epochs=5)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "W3ZVOhugCaXA" - }, - "source": [ - "モデルの訓練の進行とともに、損失値と正解率が表示されます。このモデルの場合、訓練用データでは0.88(すなわち88%)の正解率に達します。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "oEw4bZgGCaXB" - }, - "source": [ - "## 正解率の評価\n", - "\n", - "次に、テスト用データセットに対するモデルの性能を比較します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "VflXLEeECaXC" - }, - "outputs": [], - "source": [ - "test_loss, test_acc = model.evaluate(test_images, test_labels)\n", - "\n", - "print('\\nTest accuracy:', test_acc)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "yWfgsmVXCaXG" - }, - "source": [ - "ご覧の通り、テスト用データセットでの正解率は、訓練用データセットでの正解率よりも少し低くなります。この訓練時の正解率とテスト時の正解率の差は、**過学習**(over fitting)の一例です。過学習とは、新しいデータに対する機械学習モデルの性能が、訓練時と比較して低下する現象です。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "xsoS7CPDCaXH" - }, - "source": [ - "## 予測する\n", - "\n", - "モデルの訓練が終わったら、そのモデルを使って画像の分類予測を行うことが出来ます。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "Gl91RPhdCaXI" - }, - "outputs": [], - "source": [ - "predictions = model.predict(test_images)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "x9Kk1voUCaXJ" - }, - "source": [ - "これは、モデルがテスト用データセットの画像のひとつひとつを分類予測した結果です。最初の予測を見てみましょう。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "3DmJEUinCaXK" - }, - "outputs": [], - "source": [ - "predictions[0]" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "-hw1hgeSCaXN" - }, - "source": [ - "予測結果は、10個の数字の配列です。これは、その画像が10の衣料品の種類のそれぞれに該当するかの「確信度」を表しています。どのラベルが一番確信度が高いかを見てみましょう。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "qsqenuPnCaXO" - }, - "outputs": [], - "source": [ - "np.argmax(predictions[0])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "E51yS7iCCaXO" - }, - "source": [ - "というわけで、このモデルは、この画像が、アンクルブーツ、`class_names[9]` である可能性が最も高いと判断したことになります。これが正しいかどうか、テスト用ラベルを見てみましょう。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "Sd7Pgsu6CaXP" - }, - "outputs": [], - "source": [ - "test_labels[0]" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "kgdvGD52CaXR" - }, - "source": [ - "10チャンネルすべてをグラフ化してみることができます。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "VsRq6uZiG7eT" - }, - "outputs": [], - "source": [ - "def plot_image(i, predictions_array, true_label, img):\n", - " predictions_array, true_label, img = predictions_array[i], true_label[i], img[i]\n", - " plt.grid(False)\n", - " plt.xticks([])\n", - " plt.yticks([])\n", - "\n", - " plt.imshow(img, cmap=plt.cm.binary)\n", - "\n", - " predicted_label = np.argmax(predictions_array)\n", - " if predicted_label == true_label:\n", - " color = 'blue'\n", - " else:\n", - " color = 'red'\n", - "\n", - " plt.xlabel(\"{} {:2.0f}% ({})\".format(class_names[predicted_label],\n", - " 100*np.max(predictions_array),\n", - " class_names[true_label]),\n", - " color=color)\n", - "\n", - "def plot_value_array(i, predictions_array, true_label):\n", - " predictions_array, true_label = predictions_array[i], true_label[i]\n", - " plt.grid(False)\n", - " plt.xticks([])\n", - " plt.yticks([])\n", - " thisplot = plt.bar(range(10), predictions_array, color=\"#777777\")\n", - " plt.ylim([0, 1]) \n", - " predicted_label = np.argmax(predictions_array)\n", - "\n", - " thisplot[predicted_label].set_color('red')\n", - " thisplot[true_label].set_color('blue')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "aZ_jDyLZG7eW" - }, - "source": [ - "0番目の画像と、予測、予測配列を見てみましょう。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "UH_jgCxEG7eW" - }, - "outputs": [], - "source": [ - "i = 0\n", - "plt.figure(figsize=(6,3))\n", - "plt.subplot(1,2,1)\n", - "plot_image(i, predictions, test_labels, test_images)\n", - "plt.subplot(1,2,2)\n", - "plot_value_array(i, predictions, test_labels)\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "5_7K0ZL7G7eY" - }, - "outputs": [], - "source": [ - "i = 12\n", - "plt.figure(figsize=(6,3))\n", - "plt.subplot(1,2,1)\n", - "plot_image(i, predictions, test_labels, test_images)\n", - "plt.subplot(1,2,2)\n", - "plot_value_array(i, predictions, test_labels)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "Lduh0pbfG7eb" - }, - "source": [ - "予測の中のいくつかの画像を、予測値とともに表示してみましょう。正しい予測は青で、誤っている予測は赤でラベルを表示します。数字は予測したラベルのパーセント(100分率)を示します。自信があるように見えても間違っていることがあることに注意してください。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "YGBDAiziCaXR" - }, - "outputs": [], - "source": [ - "# X個のテスト画像、予測されたラベル、正解ラベルを表示します。\n", - "# 正しい予測は青で、間違った予測は赤で表示しています。\n", - "num_rows = 5\n", - "num_cols = 3\n", - "num_images = num_rows*num_cols\n", - "plt.figure(figsize=(2*2*num_cols, 2*num_rows))\n", - "for i in range(num_images):\n", - " plt.subplot(num_rows, 2*num_cols, 2*i+1)\n", - " plot_image(i, predictions, test_labels, test_images)\n", - " plt.subplot(num_rows, 2*num_cols, 2*i+2)\n", - " plot_value_array(i, predictions, test_labels)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "R32zteKHCaXT" - }, - "source": [ - "最後に、訓練済みモデルを使って1枚の画像に対する予測を行います。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "yRJ7JU7JCaXT" - }, - "outputs": [], - "source": [ - "# テスト用データセットから画像を1枚取り出す\n", - "img = test_images[0]\n", - "\n", - "print(img.shape)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "vz3bVp21CaXV" - }, - "source": [ - "`tf.keras` モデルは、サンプルの中の**バッチ**(batch)あるいは「集まり」について予測を行うように作られています。そのため、1枚の画像を使う場合でも、リスト化する必要があります。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "lDFh5yF_CaXW" - }, - "outputs": [], - "source": [ - "# 画像を1枚だけのバッチのメンバーにする\n", - "img = (np.expand_dims(img,0))\n", - "\n", - "print(img.shape)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "EQ5wLTkcCaXY" - }, - "source": [ - "そして、予測を行います。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "o_rzNSdrCaXY", - "scrolled": true - }, - "outputs": [], - "source": [ - "predictions_single = model.predict(img)\n", - "\n", - "print(predictions_single)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "6o3nwO-KG7ex" - }, - "outputs": [], - "source": [ - "plot_value_array(0, predictions_single, test_labels)\n", - "_ = plt.xticks(range(10), class_names, rotation=45)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "cU1Y2OAMCaXb" - }, - "source": [ - "`model.predict` メソッドの戻り値は、リストのリストです。リストの要素のそれぞれが、バッチの中の画像に対応します。バッチの中から、(といってもバッチの中身は1つだけですが)予測を取り出します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "2tRmdq_8CaXb" - }, - "outputs": [], - "source": [ - "np.argmax(predictions_single[0])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "YFc2HbEVCaXd" - }, - "source": [ - "というわけで、モデルは9というラベルを予測しました。" - ] - } - ], - "metadata": { - "colab": { - "collapsed_sections": [], - "name": "basic_classification.ipynb", - "private_outputs": true, - "provenance": [], - "toc_visible": true, - "version": "0.3.2" - }, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "basic_classification.ipynb", + "version": "0.3.2", + "provenance": [], + "private_outputs": true, + "collapsed_sections": [], + "toc_visible": true + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.5" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "MhoQ0WE77laV" + }, + "source": [ + "##### Copyright 2018 The TensorFlow Authors." + ] + }, + { + "cell_type": "code", + "metadata": { + "cellView": "form", + "colab_type": "code", + "id": "_ckMIh7O7s6D", + "colab": {} + }, + "source": [ + "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "cellView": "form", + "colab_type": "code", + "id": "vasWnqRgy1H4", + "colab": {} + }, + "source": [ + "#@title MIT License\n", + "#\n", + "# Copyright (c) 2017 François Chollet\n", + "#\n", + "# Permission is hereby granted, free of charge, to any person obtaining a\n", + "# copy of this software and associated documentation files (the \"Software\"),\n", + "# to deal in the Software without restriction, including without limitation\n", + "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n", + "# and/or sell copies of the Software, and to permit persons to whom the\n", + "# Software is furnished to do so, subject to the following conditions:\n", + "#\n", + "# The above copyright notice and this permission notice shall be included in\n", + "# all copies or substantial portions of the Software.\n", + "#\n", + "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n", + "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n", + "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n", + "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n", + "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n", + "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n", + "# DEALINGS IN THE SOFTWARE." + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "jYysdyb-CaWM" + }, + "source": [ + "# はじめてのニューラルネットワーク:分類問題の初歩" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "S5Uhzt6vVIB2" + }, + "source": [ + "\n", + " \n", + " \n", + " \n", + "
\n", + " View on TensorFlow.org\n", + " \n", + " Run in Google Colab\n", + " \n", + " View source on GitHub\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EQ4yfFQxW7by", + "colab_type": "text" + }, + "source": [ + "Note: これらのドキュメントは私たちTensorFlowコミュニティが翻訳したものです。コミュニティによる 翻訳は**ベストエフォート**であるため、この翻訳が正確であることや[英語の公式ドキュメント](https://www.tensorflow.org/?hl=en)の 最新の状態を反映したものであることを保証することはできません。 この翻訳の品質を向上させるためのご意見をお持ちの方は、GitHubリポジトリ[tensorflow/docs](https://github.com/tensorflow/docs)にプルリクエストをお送りください。 コミュニティによる翻訳やレビューに参加していただける方は、 [docs-ja@tensorflow.org メーリングリスト](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ja)にご連絡ください。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "FbVhjPpzn6BM" + }, + "source": [ + "このガイドでは、スニーカーやシャツなど、身に着けるものの写真を分類するニューラルネットワークのモデルを訓練します。すべての詳細を理解できなくても問題ありません。TensorFlowの全体を早足で掴むためのもので、詳細についてはあとから見ていくことになります。\n", + "\n", + "このガイドでは、TensorFlowのモデルを構築し訓練するためのハイレベルのAPIである [tf.keras](https://www.tensorflow.org/guide/keras)を使用します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "jL3OqFKZ9dFg", + "colab": {} + }, + "source": [ + "!pip install tensorflow==2.0.0-alpha0" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "dzLKpmZICaWN", + "colab": {} + }, + "source": [ + "from __future__ import absolute_import, division, print_function\n", + "\n", + "# TensorFlow と tf.keras のインポート\n", + "import tensorflow as tf\n", + "from tensorflow import keras\n", + "\n", + "# ヘルパーライブラリのインポート\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "\n", + "print(tf.__version__)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "yR0EdgrLCaWR" + }, + "source": [ + "## ファッションMNISTデータセットのロード" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "DLdCchMdCaWQ" + }, + "source": [ + "このガイドでは、[Fashion MNIST](https://github.com/zalandoresearch/fashion-mnist)を使用します。Fashion MNISTには10カテゴリーの白黒画像70,000枚が含まれています。それぞれは下図のような1枚に付き1種類の衣料品が写っている低解像度(28×28ピクセル)の画像です。\n", + "\n", + "\n", + " \n", + " \n", + "
\n", + " \"Fashion\n", + "
\n", + " Figure 1. Fashion-MNIST samples (by Zalando, MIT License).
 \n", + "
\n", + "\n", + "Fashion MNISTは、画像処理のための機械学習での\"Hello, World\"としてしばしば登場する[MNIST](http://yann.lecun.com/exdb/mnist/) データセットの代替として開発されたものです。MNISTデータセットは手書きの数字(0, 1, 2 など)から構成されており、そのフォーマットはこれから使うFashion MNISTと全く同じです。\n", + "\n", + "Fashion MNISTを使うのは、目先を変える意味もありますが、普通のMNISTよりも少しだけ手応えがあるからでもあります。どちらのデータセットも比較的小さく、アルゴリズムが期待したとおりに機能するかどうかを確かめるために使われます。プログラムのテストやデバッグのためには、よい出発点になります。\n", + "\n", + "ここでは、60,000枚の画像を訓練に、10,000枚の画像を、ネットワークが学習した画像分類の正確性を評価するのに使います。TensorFlowを使うと、下記のようにFashion MNISTのデータを簡単にインポートし、ロードすることが出来ます。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "7MqDQO0KCaWS", + "colab": {} + }, + "source": [ + "fashion_mnist = keras.datasets.fashion_mnist\n", + "\n", + "(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "t9FDsUlxCaWW" + }, + "source": [ + "ロードしたデータセットは、NumPy配列になります。\n", + "\n", + "* `train_images` と `train_labels` の2つの配列は、モデルの訓練に使用される**訓練用データセット**です。\n", + "* 訓練されたモデルは、 `test_images` と `test_labels` 配列からなる**テスト用データセット**を使ってテストします。\n", + "\n", + "画像は28×28のNumPy配列から構成されています。それぞれのピクセルの値は0から255の間の整数です。**ラベル**(label)は、0から9までの整数の配列です。それぞれの数字が下表のように、衣料品の**クラス**(class)に対応しています。\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
LabelClass
0T-shirt/top
1Trouser
2Pullover
3Dress
4Coat
5Sandal
6Shirt
7Sneaker
8Bag
9Ankle boot
\n", + "\n", + "画像はそれぞれ単一のラベルに分類されます。データセットには上記の**クラス名**が含まれていないため、後ほど画像を出力するときのために、クラス名を保存しておきます。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "IjnLH5S2CaWx", + "colab": {} + }, + "source": [ + "class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', \n", + " 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Brm0b_KACaWX" + }, + "source": [ + "## データの観察\n", + "\n", + "モデルの訓練を行う前に、データセットのフォーマットを見てみましょう。下記のように、訓練用データセットには28×28ピクセルの画像が60,000枚含まれています。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "zW5k_xz1CaWX", + "colab": {} + }, + "source": [ + "train_images.shape" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "cIAcvQqMCaWf" + }, + "source": [ + "同様に、訓練用データセットには60,000個のラベルが含まれます。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "TRFYHB2mCaWb", + "colab": {} + }, + "source": [ + "len(train_labels)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "YSlYxFuRCaWk" + }, + "source": [ + "ラベルはそれぞれ、0から9までの間の整数です。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "XKnCTHz4CaWg", + "colab": {} + }, + "source": [ + "train_labels" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "TMPI88iZpO2T" + }, + "source": [ + "テスト用データセットには、10,000枚の画像が含まれます。画像は28×28ピクセルで構成されています。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "2KFnYlcwCaWl", + "colab": {} + }, + "source": [ + "test_images.shape" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "rd0A0Iu0CaWq" + }, + "source": [ + "テスト用データセットには10,000個のラベルが含まれます。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "iJmPr5-ACaWn", + "colab": {} + }, + "source": [ + "len(test_labels)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "ES6uQoLKCaWr" + }, + "source": [ + "## データの前処理\n", + "\n", + "ネットワークを訓練する前に、データを前処理する必要があります。最初の画像を調べてみればわかるように、ピクセルの値は0から255の間の数値です。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "m4VEw8Ud9Quh", + "colab": {} + }, + "source": [ + "plt.figure()\n", + "plt.imshow(train_images[0])\n", + "plt.colorbar()\n", + "plt.grid(False)\n", + "plt.show()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "3jCZdQNNCaWv" + }, + "source": [ + "ニューラルネットワークにデータを投入する前に、これらの値を0から1までの範囲にスケールします。そのためには、画素の値を255で割ります。\n", + "\n", + "**訓練用データセット**と**テスト用データセット**は、同じように前処理することが重要です。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "bW5WzIPlCaWv", + "colab": {} + }, + "source": [ + "train_images = train_images / 255.0\n", + "\n", + "test_images = test_images / 255.0" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Ee638AlnCaWz" + }, + "source": [ + "**訓練用データセット**の最初の25枚の画像を、クラス名付きで表示してみましょう。ネットワークを構築・訓練する前に、データが正しいフォーマットになっていることを確認します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "oZTImqg_CaW1", + "colab": {} + }, + "source": [ + "plt.figure(figsize=(10,10))\n", + "for i in range(25):\n", + " plt.subplot(5,5,i+1)\n", + " plt.xticks([])\n", + " plt.yticks([])\n", + " plt.grid(False)\n", + " plt.imshow(train_images[i], cmap=plt.cm.binary)\n", + " plt.xlabel(class_names[train_labels[i]])\n", + "plt.show()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "59veuiEZCaW4" + }, + "source": [ + "## モデルの構築\n", + "\n", + "ニューラルネットワークを構築するには、まずモデルの階層を定義し、その後モデルをコンパイルします。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Gxg1XGm0eOBy" + }, + "source": [ + "### 層の設定\n", + "\n", + "ニューラルネットワークを形作る基本的な構成要素は**層**(layer)です。層は、入力されたデータから「表現」を抽出します。それらの「表現」は、今取り組もうとしている問題に対して、より「意味のある」ものであることが期待されます。\n", + "\n", + "ディープラーニングモデルのほとんどは、単純な層の積み重ねで構成されています。`tf.keras.layers.Dense` のような層のほとんどには、訓練中に学習されるパラメータが存在します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "9ODch-OFCaW4", + "colab": {} + }, + "source": [ + "model = keras.Sequential([\n", + " keras.layers.Flatten(input_shape=(28, 28)),\n", + " keras.layers.Dense(128, activation='relu'),\n", + " keras.layers.Dense(10, activation='softmax')\n", + "])" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "gut8A_7rCaW6" + }, + "source": [ + "このネットワークの最初の層は、`tf.keras.layers.Flatten` です。この層は、画像を(28×28ピクセルの)2次元配列から、28×28=784ピクセルの、1次元配列に変換します。この層が、画像の中に積まれているピクセルの行を取り崩し、横に並べると考えてください。この層には学習すべきパラメータはなく、ただデータのフォーマット変換を行うだけです。\n", + "\n", + "ピクセルが1次元化されたあと、ネットワークは2つの `tf.keras.layers.Dense` 層となります。これらの層は、密結合あるいは全結合されたニューロンの層となります。最初の `Dense` 層には、128個のノード(あるはニューロン)があります。最後の層でもある2番めの層は、10ノードの**softmax**層です。この層は、合計が1になる10個の確率の配列を返します。それぞれのノードは、今見ている画像が10個のクラスのひとつひとつに属する確率を出力します。\n", + "\n", + "### モデルのコンパイル\n", + "\n", + "モデルが訓練できるようになるには、いくつかの設定を追加する必要があります。それらの設定は、モデルの**コンパイル**(compile)時に追加されます。\n", + "\n", + "* **損失関数**(loss function) —訓練中にモデルがどれくらい正確かを測定します。この関数の値を最小化することにより、訓練中のモデルを正しい方向に向かわせようというわけです。\n", + "* **オプティマイザ**(optimizer)—モデルが見ているデータと、損失関数の値から、どのようにモデルを更新するかを決定します。\n", + "* **メトリクス**(metrics) —訓練とテストのステップを監視するのに使用します。下記の例では*accuracy* (正解率)、つまり、画像が正しく分類された比率を使用しています。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "Lhan11blCaW7", + "colab": {} + }, + "source": [ + "model.compile(optimizer='adam', \n", + " loss='sparse_categorical_crossentropy',\n", + " metrics=['accuracy'])" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "qKF6uW-BCaW-" + }, + "source": [ + "## モデルの訓練\n", + "\n", + "ニューラルネットワークの訓練には次のようなステップが必要です。\n", + "\n", + "1. モデルに訓練用データを投入します—この例では `train_images` と `train_labels` の2つの配列です。\n", + "2. モデルは、画像とラベルの対応関係を学習します。\n", + "3. モデルにテスト用データセットの予測(分類)を行わせます—この例では `test_images` 配列です。その後、予測結果と `test_labels` 配列を照合します。 \n", + "\n", + "訓練を開始するには、`model.fit` メソッドを呼び出します。モデルを訓練用データに \"fit\"(適合)させるという意味です。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "xvwvpA64CaW_", + "colab": {} + }, + "source": [ + "model.fit(train_images, train_labels, epochs=5)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "W3ZVOhugCaXA" + }, + "source": [ + "モデルの訓練の進行とともに、損失値と正解率が表示されます。このモデルの場合、訓練用データでは0.88(すなわち88%)の正解率に達します。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "oEw4bZgGCaXB" + }, + "source": [ + "## 正解率の評価\n", + "\n", + "次に、テスト用データセットに対するモデルの性能を比較します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "VflXLEeECaXC", + "colab": {} + }, + "source": [ + "test_loss, test_acc = model.evaluate(test_images, test_labels)\n", + "\n", + "print('\\nTest accuracy:', test_acc)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "yWfgsmVXCaXG" + }, + "source": [ + "ご覧の通り、テスト用データセットでの正解率は、訓練用データセットでの正解率よりも少し低くなります。この訓練時の正解率とテスト時の正解率の差は、**過学習**(over fitting)の一例です。過学習とは、新しいデータに対する機械学習モデルの性能が、訓練時と比較して低下する現象です。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "xsoS7CPDCaXH" + }, + "source": [ + "## 予測する\n", + "\n", + "モデルの訓練が終わったら、そのモデルを使って画像の分類予測を行うことが出来ます。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "Gl91RPhdCaXI", + "colab": {} + }, + "source": [ + "predictions = model.predict(test_images)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "x9Kk1voUCaXJ" + }, + "source": [ + "これは、モデルがテスト用データセットの画像のひとつひとつを分類予測した結果です。最初の予測を見てみましょう。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "3DmJEUinCaXK", + "colab": {} + }, + "source": [ + "predictions[0]" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "-hw1hgeSCaXN" + }, + "source": [ + "予測結果は、10個の数字の配列です。これは、その画像が10の衣料品の種類のそれぞれに該当するかの「確信度」を表しています。どのラベルが一番確信度が高いかを見てみましょう。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "qsqenuPnCaXO", + "colab": {} + }, + "source": [ + "np.argmax(predictions[0])" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "E51yS7iCCaXO" + }, + "source": [ + "というわけで、このモデルは、この画像が、アンクルブーツ、`class_names[9]` である可能性が最も高いと判断したことになります。これが正しいかどうか、テスト用ラベルを見てみましょう。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "Sd7Pgsu6CaXP", + "colab": {} + }, + "source": [ + "test_labels[0]" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "kgdvGD52CaXR" + }, + "source": [ + "10チャンネルすべてをグラフ化してみることができます。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "VsRq6uZiG7eT", + "colab": {} + }, + "source": [ + "def plot_image(i, predictions_array, true_label, img):\n", + " predictions_array, true_label, img = predictions_array[i], true_label[i], img[i]\n", + " plt.grid(False)\n", + " plt.xticks([])\n", + " plt.yticks([])\n", + "\n", + " plt.imshow(img, cmap=plt.cm.binary)\n", + "\n", + " predicted_label = np.argmax(predictions_array)\n", + " if predicted_label == true_label:\n", + " color = 'blue'\n", + " else:\n", + " color = 'red'\n", + "\n", + " plt.xlabel(\"{} {:2.0f}% ({})\".format(class_names[predicted_label],\n", + " 100*np.max(predictions_array),\n", + " class_names[true_label]),\n", + " color=color)\n", + "\n", + "def plot_value_array(i, predictions_array, true_label):\n", + " predictions_array, true_label = predictions_array[i], true_label[i]\n", + " plt.grid(False)\n", + " plt.xticks([])\n", + " plt.yticks([])\n", + " thisplot = plt.bar(range(10), predictions_array, color=\"#777777\")\n", + " plt.ylim([0, 1]) \n", + " predicted_label = np.argmax(predictions_array)\n", + "\n", + " thisplot[predicted_label].set_color('red')\n", + " thisplot[true_label].set_color('blue')" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "aZ_jDyLZG7eW" + }, + "source": [ + "0番目の画像と、予測、予測配列を見てみましょう。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "UH_jgCxEG7eW", + "colab": {} + }, + "source": [ + "i = 0\n", + "plt.figure(figsize=(6,3))\n", + "plt.subplot(1,2,1)\n", + "plot_image(i, predictions, test_labels, test_images)\n", + "plt.subplot(1,2,2)\n", + "plot_value_array(i, predictions, test_labels)\n", + "plt.show()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "5_7K0ZL7G7eY", + "colab": {} + }, + "source": [ + "i = 12\n", + "plt.figure(figsize=(6,3))\n", + "plt.subplot(1,2,1)\n", + "plot_image(i, predictions, test_labels, test_images)\n", + "plt.subplot(1,2,2)\n", + "plot_value_array(i, predictions, test_labels)\n", + "plt.show()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Lduh0pbfG7eb" + }, + "source": [ + "予測の中のいくつかの画像を、予測値とともに表示してみましょう。正しい予測は青で、誤っている予測は赤でラベルを表示します。数字は予測したラベルのパーセント(100分率)を示します。自信があるように見えても間違っていることがあることに注意してください。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "YGBDAiziCaXR", + "colab": {} + }, + "source": [ + "# X個のテスト画像、予測されたラベル、正解ラベルを表示します。\n", + "# 正しい予測は青で、間違った予測は赤で表示しています。\n", + "num_rows = 5\n", + "num_cols = 3\n", + "num_images = num_rows*num_cols\n", + "plt.figure(figsize=(2*2*num_cols, 2*num_rows))\n", + "for i in range(num_images):\n", + " plt.subplot(num_rows, 2*num_cols, 2*i+1)\n", + " plot_image(i, predictions, test_labels, test_images)\n", + " plt.subplot(num_rows, 2*num_cols, 2*i+2)\n", + " plot_value_array(i, predictions, test_labels)\n", + "plt.show()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "R32zteKHCaXT" + }, + "source": [ + "最後に、訓練済みモデルを使って1枚の画像に対する予測を行います。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "yRJ7JU7JCaXT", + "colab": {} + }, + "source": [ + "# テスト用データセットから画像を1枚取り出す\n", + "img = test_images[0]\n", + "\n", + "print(img.shape)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "vz3bVp21CaXV" + }, + "source": [ + "`tf.keras` モデルは、サンプルの中の**バッチ**(batch)あるいは「集まり」について予測を行うように作られています。そのため、1枚の画像を使う場合でも、リスト化する必要があります。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "lDFh5yF_CaXW", + "colab": {} + }, + "source": [ + "# 画像を1枚だけのバッチのメンバーにする\n", + "img = (np.expand_dims(img,0))\n", + "\n", + "print(img.shape)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "EQ5wLTkcCaXY" + }, + "source": [ + "そして、予測を行います。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "o_rzNSdrCaXY", + "scrolled": true, + "colab": {} + }, + "source": [ + "predictions_single = model.predict(img)\n", + "\n", + "print(predictions_single)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "6o3nwO-KG7ex", + "colab": {} + }, + "source": [ + "plot_value_array(0, predictions_single, test_labels)\n", + "_ = plt.xticks(range(10), class_names, rotation=45)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "cU1Y2OAMCaXb" + }, + "source": [ + "`model.predict` メソッドの戻り値は、リストのリストです。リストの要素のそれぞれが、バッチの中の画像に対応します。バッチの中から、(といってもバッチの中身は1つだけですが)予測を取り出します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "2tRmdq_8CaXb", + "colab": {} + }, + "source": [ + "np.argmax(predictions_single[0])" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "YFc2HbEVCaXd" + }, + "source": [ + "というわけで、モデルは9というラベルを予測しました。" + ] + } + ] +} \ No newline at end of file diff --git a/site/ja/r2/tutorials/keras/basic_regression.ipynb b/site/ja/r2/tutorials/keras/basic_regression.ipynb index 5f097f637f8..42c39ab7b75 100644 --- a/site/ja/r2/tutorials/keras/basic_regression.ipynb +++ b/site/ja/r2/tutorials/keras/basic_regression.ipynb @@ -1,866 +1,876 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "FhGuhbZ6M5tl" - }, - "source": [ - "##### Copyright 2018 The TensorFlow Authors." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "cellView": "form", - "colab": {}, - "colab_type": "code", - "id": "AwOEIRJC6Une" - }, - "outputs": [], - "source": [ - "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", - "# you may not use this file except in compliance with the License.\n", - "# You may obtain a copy of the License at\n", - "#\n", - "# https://www.apache.org/licenses/LICENSE-2.0\n", - "#\n", - "# Unless required by applicable law or agreed to in writing, software\n", - "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", - "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", - "# See the License for the specific language governing permissions and\n", - "# limitations under the License." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "cellView": "form", - "colab": {}, - "colab_type": "code", - "id": "KyPEtTqk6VdG" - }, - "outputs": [], - "source": [ - "#@title MIT License\n", - "#\n", - "# Copyright (c) 2017 François Chollet\n", - "#\n", - "# Permission is hereby granted, free of charge, to any person obtaining a\n", - "# copy of this software and associated documentation files (the \"Software\"),\n", - "# to deal in the Software without restriction, including without limitation\n", - "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n", - "# and/or sell copies of the Software, and to permit persons to whom the\n", - "# Software is furnished to do so, subject to the following conditions:\n", - "#\n", - "# The above copyright notice and this permission notice shall be included in\n", - "# all copies or substantial portions of the Software.\n", - "#\n", - "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n", - "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n", - "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n", - "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n", - "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n", - "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n", - "# DEALINGS IN THE SOFTWARE." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "EIdT9iu_Z4Rb" - }, - "source": [ - "# 回帰:燃費を予測する " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "bBIlTPscrIT9" - }, - "source": [ - "\n", - " \n", - " \n", - " \n", - "
\n", - " View on TensorFlow.org\n", - " \n", - " Run in Google Colab\n", - " \n", - " View source on GitHub\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "AHp3M9ZmrIxj" - }, - "source": [ - "回帰問題では、価格や確率といった連続的な値の出力を予測することが目的となります。これは、分類問題の目的が、(例えば、写真にリンゴが写っているかオレンジが写っているかといった)離散的なラベルを予測することであるのとは対照的です。\n", - "\n", - "このノートブックでは、古典的な[Auto MPG](https://archive.ics.uci.edu/ml/datasets/auto+mpg)データセットを使用し、1970年代後半から1980年台初めの自動車の燃費を予測するモデルを構築します。この目的のため、モデルにはこの時期の多数の自動車の仕様を読み込ませます。仕様には、気筒数、排気量、馬力、重量などが含まれています。\n", - "\n", - "このサンプルでは`tf.keras` APIを使用しています。詳細は[このガイド](https://www.tensorflow.org/guide/keras)を参照してください。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "moB4tpEHxKB3" - }, - "outputs": [], - "source": [ - "# ペアプロットのためseabornを使用します\n", - "!pip install seaborn" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "1rRo8oNqZ-Rj" - }, - "outputs": [], - "source": [ - "from __future__ import absolute_import, division, print_function\n", - "\n", - "import pathlib\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import pandas as pd\n", - "import seaborn as sns\n", - "\n", - "!pip install tensorflow==2.0.0-alpha0\n", - "import tensorflow as tf\n", - "\n", - "from tensorflow import keras\n", - "from tensorflow.keras import layers\n", - "\n", - "print(tf.__version__)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "F_72b0LCNbjx" - }, - "source": [ - "## Auto MPG データセット\n", - "\n", - "このデータセットは[UCI Machine Learning Repository](https://archive.ics.uci.edu/)から入手可能です。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "gFh9ne3FZ-On" - }, - "source": [ - "### データの取得\n", - "\n", - "まず、データセットをダウンロードします。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "p9kxxgzvzlyz" - }, - "outputs": [], - "source": [ - "dataset_path = keras.utils.get_file(\"auto-mpg.data\", \"https://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data\")\n", - "dataset_path" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "nslsRLh7Zss4" - }, - "source": [ - "pandasを使ってデータをインポートします。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "CiX2FI4gZtTt" - }, - "outputs": [], - "source": [ - "column_names = ['MPG','Cylinders','Displacement','Horsepower','Weight',\n", - " 'Acceleration', 'Model Year', 'Origin'] \n", - "raw_dataset = pd.read_csv(dataset_path, names=column_names,\n", - " na_values = \"?\", comment='\\t',\n", - " sep=\" \", skipinitialspace=True)\n", - "\n", - "dataset = raw_dataset.copy()\n", - "dataset.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "3MWuJTKEDM-f" - }, - "source": [ - "### データのクレンジング\n", - "\n", - "このデータには、いくつか欠損値があります。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "JEJHhN65a2VV" - }, - "outputs": [], - "source": [ - "dataset.isna().sum()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "9UPN0KBHa_WI" - }, - "source": [ - "この最初のチュートリアルでは簡単化のためこれらの行を削除します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "4ZUDosChC1UN" - }, - "outputs": [], - "source": [ - "dataset = dataset.dropna()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "8XKitwaH4v8h" - }, - "source": [ - "`\"Origin\"`の列は数値ではなくカテゴリーです。このため、ワンホットエンコーディングを行います。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "gWNTD2QjBWFJ" - }, - "outputs": [], - "source": [ - "origin = dataset.pop('Origin')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "ulXz4J7PAUzk" - }, - "outputs": [], - "source": [ - "dataset['USA'] = (origin == 1)*1.0\n", - "dataset['Europe'] = (origin == 2)*1.0\n", - "dataset['Japan'] = (origin == 3)*1.0\n", - "dataset.tail()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "Cuym4yvk76vU" - }, - "source": [ - "### データを訓練用セットとテスト用セットに分割\n", - "\n", - "データセットを訓練用セットとテスト用セットに分割しましょう。\n", - "\n", - "テスト用データセットは、作成したモデルの最終評価に使用します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "qn-IGhUE7_1H" - }, - "outputs": [], - "source": [ - "train_dataset = dataset.sample(frac=0.8,random_state=0)\n", - "test_dataset = dataset.drop(train_dataset.index)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "J4ubs136WLNp" - }, - "source": [ - "### データの観察\n", - "\n", - "訓練用セットのいくつかの列の組み合わせの同時分布を見てみましょう。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "oRKO_x8gWKv-" - }, - "outputs": [], - "source": [ - "sns.pairplot(train_dataset[[\"MPG\", \"Cylinders\", \"Displacement\", \"Weight\"]], diag_kind=\"kde\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "gavKO_6DWRMP" - }, - "source": [ - "全体の統計値も見てみましょう。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "yi2FzC3T21jR" - }, - "outputs": [], - "source": [ - "train_stats = train_dataset.describe()\n", - "train_stats.pop(\"MPG\")\n", - "train_stats = train_stats.transpose()\n", - "train_stats" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "Db7Auq1yXUvh" - }, - "source": [ - "### ラベルと特徴量の分離\n", - "\n", - "ラベル、すなわち目的変数を特徴量から切り離しましょう。このラベルは、モデルに予測させたい数量です。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "t2sluJdCW7jN" - }, - "outputs": [], - "source": [ - "train_labels = train_dataset.pop('MPG')\n", - "test_labels = test_dataset.pop('MPG')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "mRklxK5s388r" - }, - "source": [ - "### データの正規化\n", - "\n", - "上の`train_stats`のブロックをもう一度見て、それぞれの特徴量の範囲がどれほど違っているかに注目してください。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "-ywmerQ6dSox" - }, - "source": [ - "スケールや値の範囲が異なる特徴量を正規化するのは良い習慣です。特徴量の正規化なしでもモデルは収束する**かもしれませんが**、モデルの訓練はより難しくなり、結果として得られたモデルも入力で使われる単位に依存することになります。\n", - "\n", - "注:(正規化に使用する)統計量は意図的に訓練用データセットだけを使って算出していますが、これらはテスト用データセットの正規化にも使うことになります。テスト用のデータセットを、モデルの訓練に使用した分布と同じ分布に射影する必要があるのです。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "JlC5ooJrgjQF" - }, - "outputs": [], - "source": [ - "def norm(x):\n", - " return (x - train_stats['mean']) / train_stats['std']\n", - "normed_train_data = norm(train_dataset)\n", - "normed_test_data = norm(test_dataset)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "BuiClDk45eS4" - }, - "source": [ - "この正規化したデータを使ってモデルを訓練することになります。\n", - "\n", - "注意:ここで入力の正規化に使った統計量(平均と標準偏差)は、先程実施したワンホットエンコーディングとともに、モデルに供給する他のどんなデータにも適用する必要があります。テスト用データセットだけでなく、モデルを本番で使用する際の生のデータも同様です。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "SmjdzxKzEu1-" - }, - "source": [ - "## モデル" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "6SWtkIjhrZwa" - }, - "source": [ - "### モデルの構築\n", - "\n", - "それではモデルを構築しましょう。ここでは、2つの全結合の隠れ層と、1つの連続値を返す出力層からなる、`Sequential`モデルを使います。モデルを構築するステップは`build_model`という1つの関数の中に組み込みます。あとから2つ目のモデルを構築するためです。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "c26juK7ZG8j-" - }, - "outputs": [], - "source": [ - "def build_model():\n", - " model = keras.Sequential([\n", - " layers.Dense(64, activation='relu', input_shape=[len(train_dataset.keys())]),\n", - " layers.Dense(64, activation='relu'),\n", - " layers.Dense(1)\n", - " ])\n", - "\n", - " optimizer = tf.keras.optimizers.RMSprop(0.001)\n", - "\n", - " model.compile(loss='mse',\n", - " optimizer=optimizer,\n", - " metrics=['mae', 'mse'])\n", - " return model" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "cGbPb-PHGbhs" - }, - "outputs": [], - "source": [ - "model = build_model()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "Sj49Og4YGULr" - }, - "source": [ - "### モデルの検証\n", - "\n", - "`.summary`メソッドを使って、モデルの簡単な説明を表示します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "ReAD0n6MsFK-" - }, - "outputs": [], - "source": [ - "model.summary()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "Vt6W50qGsJAL" - }, - "source": [ - "では、モデルを試してみましょう。訓練用データのうち`10`個のサンプルからなるバッチを取り出し、それを使って`model.predict`メソッドを呼び出します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "-d-gBaVtGTSC" - }, - "outputs": [], - "source": [ - "example_batch = normed_train_data[:10]\n", - "example_result = model.predict(example_batch)\n", - "example_result" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "QlM8KrSOsaYo" - }, - "source": [ - "うまく動作しているようです。予定通りの型と形状の出力が得られています。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "0-qWCsh6DlyH" - }, - "source": [ - "### モデルの訓練\n", - "\n", - "モデルを1000エポック訓練し、訓練と検証の正解率を`history`オブジェクトに記録します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "sD7qHCmNIOY0" - }, - "outputs": [], - "source": [ - "# エポックが終わるごとにドットを一つ出力することで進捗を表示\n", - "class PrintDot(keras.callbacks.Callback):\n", - " def on_epoch_end(self, epoch, logs):\n", - " if epoch % 100 == 0: print('')\n", - " print('.', end='')\n", - "\n", - "EPOCHS = 1000\n", - "\n", - "history = model.fit(\n", - " normed_train_data, train_labels,\n", - " epochs=EPOCHS, validation_split = 0.2, verbose=0,\n", - " callbacks=[PrintDot()])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "tQm3pc0FYPQB" - }, - "source": [ - "`history`オブジェクトに保存された数値を使ってモデルの訓練の様子を可視化します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "4Xj91b-dymEy" - }, - "outputs": [], - "source": [ - "hist = pd.DataFrame(history.history)\n", - "hist['epoch'] = history.epoch\n", - "hist.tail()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "B6XriGbVPh2t" - }, - "outputs": [], - "source": [ - "def plot_history(history):\n", - " hist = pd.DataFrame(history.history)\n", - " hist['epoch'] = history.epoch\n", - " \n", - " plt.figure()\n", - " plt.xlabel('Epoch')\n", - " plt.ylabel('Mean Abs Error [MPG]')\n", - " plt.plot(hist['epoch'], hist['mae'],\n", - " label='Train Error')\n", - " plt.plot(hist['epoch'], hist['val_mae'],\n", - " label = 'Val Error')\n", - " plt.ylim([0,5])\n", - " plt.legend()\n", - " \n", - " plt.figure()\n", - " plt.xlabel('Epoch')\n", - " plt.ylabel('Mean Square Error [$MPG^2$]')\n", - " plt.plot(hist['epoch'], hist['mse'],\n", - " label='Train Error')\n", - " plt.plot(hist['epoch'], hist['val_mse'],\n", - " label = 'Val Error')\n", - " plt.ylim([0,20])\n", - " plt.legend()\n", - " plt.show()\n", - "\n", - "\n", - "plot_history(history)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "AqsuANc11FYv" - }, - "source": [ - "このグラフを見ると、検証エラーは100エポックを過ぎたあたりで改善が見られなくなり、むしろ悪化しているようです。検証スコアの改善が見られなくなったら自動的に訓練を停止するように、`model.fit`メソッド呼び出しを変更します。ここでは、エポック毎に訓練状態をチェックする*EarlyStopping*コールバックを使用します。設定したエポック数の間に改善が見られない場合、訓練を自動的に停止します。\n", - "\n", - "このコールバックについての詳細は[ここ](https://www.tensorflow.org/versions/master/api_docs/python/tf/keras/callbacks/EarlyStopping)を参照ください。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "fdMZuhUgzMZ4" - }, - "outputs": [], - "source": [ - "model = build_model()\n", - "\n", - "# The patience parameter is the amount of epochs to check for improvement\n", - "early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=10)\n", - "\n", - "history = model.fit(normed_train_data, train_labels, epochs=EPOCHS,\n", - " validation_split = 0.2, verbose=0, callbacks=[early_stop, PrintDot()])\n", - "\n", - "plot_history(history)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "3St8-DmrX8P4" - }, - "source": [ - "検証用データセットでのグラフを見ると、平均誤差は+/- 2 MPG(マイル/ガロン)前後です。これは良い精度でしょうか?その判断はおまかせします。\n", - "\n", - "モデルの訓練に使用していない**テスト用**データセットを使って、モデルがどれくらい汎化できているか見てみましょう。これによって、モデルが実際の現場でどれくらい正確に予測できるかがわかります。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "jl_yNr5n1kms" - }, - "outputs": [], - "source": [ - "loss, mae, mse = model.evaluate(normed_test_data, test_labels, verbose=0)\n", - "\n", - "print(\"Testing set Mean Abs Error: {:5.2f} MPG\".format(mae))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "ft603OzXuEZC" - }, - "source": [ - "### モデルを使った予測\n", - "\n", - "最後に、テストデータを使ってMPG値を予測します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "Xe7RXH3N3CWU" - }, - "outputs": [], - "source": [ - "test_predictions = model.predict(normed_test_data).flatten()\n", - "\n", - "plt.scatter(test_labels, test_predictions)\n", - "plt.xlabel('True Values [MPG]')\n", - "plt.ylabel('Predictions [MPG]')\n", - "plt.axis('equal')\n", - "plt.axis('square')\n", - "plt.xlim([0,plt.xlim()[1]])\n", - "plt.ylim([0,plt.ylim()[1]])\n", - "_ = plt.plot([-100, 100], [-100, 100])\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "OrkHGKZcusUo" - }, - "source": [ - "そこそこ良い予測ができているように見えます。誤差の分布を見てみましょう。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "f-OHX4DiXd8x" - }, - "outputs": [], - "source": [ - "error = test_predictions - test_labels\n", - "plt.hist(error, bins = 25)\n", - "plt.xlabel(\"Prediction Error [MPG]\")\n", - "_ = plt.ylabel(\"Count\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "r9_kI6MHu1UU" - }, - "source": [ - "とても正規分布には見えませんが、サンプル数が非常に小さいからだと考えられます。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "vgGQuV-yqYZH" - }, - "source": [ - "## 結論\n", - "\n", - "このノートブックでは、回帰問題を扱うためのテクニックをいくつか紹介しました。\n", - "\n", - "* 平均二乗誤差(MSE: Mean Squared Error)は回帰問題に使われる一般的な損失関数です(分類問題には異なる損失関数が使われます)。\n", - "* 同様に、回帰問題に使われる評価指標も分類問題とは異なります。回帰問題の一般的な評価指標は平均絶対誤差(MAE: Mean Absolute Error)です。\n", - "* 入力数値特徴量の範囲が異なっている場合、特徴量毎に同じ範囲に正規化するべきです。\n", - "* 訓練用データが多くない場合、過学習を避けるために少ない隠れ層を持つ小さいネットワークを使うというのが良い方策の1つです。\n", - "* Early Stoppingは過学習を防止するための便利な手法の一つです。" - ] - } - ], - "metadata": { - "colab": { - "collapsed_sections": [], - "name": "basic_regression.ipynb", - "private_outputs": true, - "provenance": [], - "toc_visible": true, - "version": "0.3.2" - }, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "basic_regression.ipynb", + "version": "0.3.2", + "provenance": [], + "private_outputs": true, + "collapsed_sections": [], + "toc_visible": true + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.5" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "FhGuhbZ6M5tl" + }, + "source": [ + "##### Copyright 2018 The TensorFlow Authors." + ] + }, + { + "cell_type": "code", + "metadata": { + "cellView": "form", + "colab_type": "code", + "id": "AwOEIRJC6Une", + "colab": {} + }, + "source": [ + "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "cellView": "form", + "colab_type": "code", + "id": "KyPEtTqk6VdG", + "colab": {} + }, + "source": [ + "#@title MIT License\n", + "#\n", + "# Copyright (c) 2017 François Chollet\n", + "#\n", + "# Permission is hereby granted, free of charge, to any person obtaining a\n", + "# copy of this software and associated documentation files (the \"Software\"),\n", + "# to deal in the Software without restriction, including without limitation\n", + "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n", + "# and/or sell copies of the Software, and to permit persons to whom the\n", + "# Software is furnished to do so, subject to the following conditions:\n", + "#\n", + "# The above copyright notice and this permission notice shall be included in\n", + "# all copies or substantial portions of the Software.\n", + "#\n", + "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n", + "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n", + "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n", + "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n", + "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n", + "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n", + "# DEALINGS IN THE SOFTWARE." + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "EIdT9iu_Z4Rb" + }, + "source": [ + "# 回帰:燃費を予測する " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "bBIlTPscrIT9" + }, + "source": [ + "\n", + " \n", + " \n", + " \n", + "
\n", + " View on TensorFlow.org\n", + " \n", + " Run in Google Colab\n", + " \n", + " View source on GitHub\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tudzcncJXetB", + "colab_type": "text" + }, + "source": [ + "Note: これらのドキュメントは私たちTensorFlowコミュニティが翻訳したものです。コミュニティによる 翻訳は**ベストエフォート**であるため、この翻訳が正確であることや[英語の公式ドキュメント](https://www.tensorflow.org/?hl=en)の 最新の状態を反映したものであることを保証することはできません。 この翻訳の品質を向上させるためのご意見をお持ちの方は、GitHubリポジトリ[tensorflow/docs](https://github.com/tensorflow/docs)にプルリクエストをお送りください。 コミュニティによる翻訳やレビューに参加していただける方は、 [docs-ja@tensorflow.org メーリングリスト](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ja)にご連絡ください。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "AHp3M9ZmrIxj" + }, + "source": [ + "回帰問題では、価格や確率といった連続的な値の出力を予測することが目的となります。これは、分類問題の目的が、(例えば、写真にリンゴが写っているかオレンジが写っているかといった)離散的なラベルを予測することであるのとは対照的です。\n", + "\n", + "このノートブックでは、古典的な[Auto MPG](https://archive.ics.uci.edu/ml/datasets/auto+mpg)データセットを使用し、1970年代後半から1980年台初めの自動車の燃費を予測するモデルを構築します。この目的のため、モデルにはこの時期の多数の自動車の仕様を読み込ませます。仕様には、気筒数、排気量、馬力、重量などが含まれています。\n", + "\n", + "このサンプルでは`tf.keras` APIを使用しています。詳細は[このガイド](https://www.tensorflow.org/guide/keras)を参照してください。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "moB4tpEHxKB3", + "colab": {} + }, + "source": [ + "# ペアプロットのためseabornを使用します\n", + "!pip install seaborn" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "1rRo8oNqZ-Rj", + "colab": {} + }, + "source": [ + "from __future__ import absolute_import, division, print_function\n", + "\n", + "import pathlib\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import pandas as pd\n", + "import seaborn as sns\n", + "\n", + "!pip install tensorflow==2.0.0-alpha0\n", + "import tensorflow as tf\n", + "\n", + "from tensorflow import keras\n", + "from tensorflow.keras import layers\n", + "\n", + "print(tf.__version__)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "F_72b0LCNbjx" + }, + "source": [ + "## Auto MPG データセット\n", + "\n", + "このデータセットは[UCI Machine Learning Repository](https://archive.ics.uci.edu/)から入手可能です。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "gFh9ne3FZ-On" + }, + "source": [ + "### データの取得\n", + "\n", + "まず、データセットをダウンロードします。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "p9kxxgzvzlyz", + "colab": {} + }, + "source": [ + "dataset_path = keras.utils.get_file(\"auto-mpg.data\", \"https://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data\")\n", + "dataset_path" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "nslsRLh7Zss4" + }, + "source": [ + "pandasを使ってデータをインポートします。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "CiX2FI4gZtTt", + "colab": {} + }, + "source": [ + "column_names = ['MPG','Cylinders','Displacement','Horsepower','Weight',\n", + " 'Acceleration', 'Model Year', 'Origin'] \n", + "raw_dataset = pd.read_csv(dataset_path, names=column_names,\n", + " na_values = \"?\", comment='\\t',\n", + " sep=\" \", skipinitialspace=True)\n", + "\n", + "dataset = raw_dataset.copy()\n", + "dataset.tail()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "3MWuJTKEDM-f" + }, + "source": [ + "### データのクレンジング\n", + "\n", + "このデータには、いくつか欠損値があります。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "JEJHhN65a2VV", + "colab": {} + }, + "source": [ + "dataset.isna().sum()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "9UPN0KBHa_WI" + }, + "source": [ + "この最初のチュートリアルでは簡単化のためこれらの行を削除します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "4ZUDosChC1UN", + "colab": {} + }, + "source": [ + "dataset = dataset.dropna()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "8XKitwaH4v8h" + }, + "source": [ + "`\"Origin\"`の列は数値ではなくカテゴリーです。このため、ワンホットエンコーディングを行います。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "gWNTD2QjBWFJ", + "colab": {} + }, + "source": [ + "origin = dataset.pop('Origin')" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "ulXz4J7PAUzk", + "colab": {} + }, + "source": [ + "dataset['USA'] = (origin == 1)*1.0\n", + "dataset['Europe'] = (origin == 2)*1.0\n", + "dataset['Japan'] = (origin == 3)*1.0\n", + "dataset.tail()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Cuym4yvk76vU" + }, + "source": [ + "### データを訓練用セットとテスト用セットに分割\n", + "\n", + "データセットを訓練用セットとテスト用セットに分割しましょう。\n", + "\n", + "テスト用データセットは、作成したモデルの最終評価に使用します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "qn-IGhUE7_1H", + "colab": {} + }, + "source": [ + "train_dataset = dataset.sample(frac=0.8,random_state=0)\n", + "test_dataset = dataset.drop(train_dataset.index)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "J4ubs136WLNp" + }, + "source": [ + "### データの観察\n", + "\n", + "訓練用セットのいくつかの列の組み合わせの同時分布を見てみましょう。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "oRKO_x8gWKv-", + "colab": {} + }, + "source": [ + "sns.pairplot(train_dataset[[\"MPG\", \"Cylinders\", \"Displacement\", \"Weight\"]], diag_kind=\"kde\")" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "gavKO_6DWRMP" + }, + "source": [ + "全体の統計値も見てみましょう。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "yi2FzC3T21jR", + "colab": {} + }, + "source": [ + "train_stats = train_dataset.describe()\n", + "train_stats.pop(\"MPG\")\n", + "train_stats = train_stats.transpose()\n", + "train_stats" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Db7Auq1yXUvh" + }, + "source": [ + "### ラベルと特徴量の分離\n", + "\n", + "ラベル、すなわち目的変数を特徴量から切り離しましょう。このラベルは、モデルに予測させたい数量です。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "t2sluJdCW7jN", + "colab": {} + }, + "source": [ + "train_labels = train_dataset.pop('MPG')\n", + "test_labels = test_dataset.pop('MPG')" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "mRklxK5s388r" + }, + "source": [ + "### データの正規化\n", + "\n", + "上の`train_stats`のブロックをもう一度見て、それぞれの特徴量の範囲がどれほど違っているかに注目してください。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "-ywmerQ6dSox" + }, + "source": [ + "スケールや値の範囲が異なる特徴量を正規化するのは良い習慣です。特徴量の正規化なしでもモデルは収束する**かもしれませんが**、モデルの訓練はより難しくなり、結果として得られたモデルも入力で使われる単位に依存することになります。\n", + "\n", + "注:(正規化に使用する)統計量は意図的に訓練用データセットだけを使って算出していますが、これらはテスト用データセットの正規化にも使うことになります。テスト用のデータセットを、モデルの訓練に使用した分布と同じ分布に射影する必要があるのです。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "JlC5ooJrgjQF", + "colab": {} + }, + "source": [ + "def norm(x):\n", + " return (x - train_stats['mean']) / train_stats['std']\n", + "normed_train_data = norm(train_dataset)\n", + "normed_test_data = norm(test_dataset)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "BuiClDk45eS4" + }, + "source": [ + "この正規化したデータを使ってモデルを訓練することになります。\n", + "\n", + "注意:ここで入力の正規化に使った統計量(平均と標準偏差)は、先程実施したワンホットエンコーディングとともに、モデルに供給する他のどんなデータにも適用する必要があります。テスト用データセットだけでなく、モデルを本番で使用する際の生のデータも同様です。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "SmjdzxKzEu1-" + }, + "source": [ + "## モデル" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "6SWtkIjhrZwa" + }, + "source": [ + "### モデルの構築\n", + "\n", + "それではモデルを構築しましょう。ここでは、2つの全結合の隠れ層と、1つの連続値を返す出力層からなる、`Sequential`モデルを使います。モデルを構築するステップは`build_model`という1つの関数の中に組み込みます。あとから2つ目のモデルを構築するためです。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "c26juK7ZG8j-", + "colab": {} + }, + "source": [ + "def build_model():\n", + " model = keras.Sequential([\n", + " layers.Dense(64, activation='relu', input_shape=[len(train_dataset.keys())]),\n", + " layers.Dense(64, activation='relu'),\n", + " layers.Dense(1)\n", + " ])\n", + "\n", + " optimizer = tf.keras.optimizers.RMSprop(0.001)\n", + "\n", + " model.compile(loss='mse',\n", + " optimizer=optimizer,\n", + " metrics=['mae', 'mse'])\n", + " return model" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "cGbPb-PHGbhs", + "colab": {} + }, + "source": [ + "model = build_model()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Sj49Og4YGULr" + }, + "source": [ + "### モデルの検証\n", + "\n", + "`.summary`メソッドを使って、モデルの簡単な説明を表示します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "ReAD0n6MsFK-", + "colab": {} + }, + "source": [ + "model.summary()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Vt6W50qGsJAL" + }, + "source": [ + "では、モデルを試してみましょう。訓練用データのうち`10`個のサンプルからなるバッチを取り出し、それを使って`model.predict`メソッドを呼び出します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "-d-gBaVtGTSC", + "colab": {} + }, + "source": [ + "example_batch = normed_train_data[:10]\n", + "example_result = model.predict(example_batch)\n", + "example_result" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "QlM8KrSOsaYo" + }, + "source": [ + "うまく動作しているようです。予定通りの型と形状の出力が得られています。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "0-qWCsh6DlyH" + }, + "source": [ + "### モデルの訓練\n", + "\n", + "モデルを1000エポック訓練し、訓練と検証の正解率を`history`オブジェクトに記録します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "sD7qHCmNIOY0", + "colab": {} + }, + "source": [ + "# エポックが終わるごとにドットを一つ出力することで進捗を表示\n", + "class PrintDot(keras.callbacks.Callback):\n", + " def on_epoch_end(self, epoch, logs):\n", + " if epoch % 100 == 0: print('')\n", + " print('.', end='')\n", + "\n", + "EPOCHS = 1000\n", + "\n", + "history = model.fit(\n", + " normed_train_data, train_labels,\n", + " epochs=EPOCHS, validation_split = 0.2, verbose=0,\n", + " callbacks=[PrintDot()])" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "tQm3pc0FYPQB" + }, + "source": [ + "`history`オブジェクトに保存された数値を使ってモデルの訓練の様子を可視化します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "4Xj91b-dymEy", + "colab": {} + }, + "source": [ + "hist = pd.DataFrame(history.history)\n", + "hist['epoch'] = history.epoch\n", + "hist.tail()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "B6XriGbVPh2t", + "colab": {} + }, + "source": [ + "def plot_history(history):\n", + " hist = pd.DataFrame(history.history)\n", + " hist['epoch'] = history.epoch\n", + " \n", + " plt.figure()\n", + " plt.xlabel('Epoch')\n", + " plt.ylabel('Mean Abs Error [MPG]')\n", + " plt.plot(hist['epoch'], hist['mae'],\n", + " label='Train Error')\n", + " plt.plot(hist['epoch'], hist['val_mae'],\n", + " label = 'Val Error')\n", + " plt.ylim([0,5])\n", + " plt.legend()\n", + " \n", + " plt.figure()\n", + " plt.xlabel('Epoch')\n", + " plt.ylabel('Mean Square Error [$MPG^2$]')\n", + " plt.plot(hist['epoch'], hist['mse'],\n", + " label='Train Error')\n", + " plt.plot(hist['epoch'], hist['val_mse'],\n", + " label = 'Val Error')\n", + " plt.ylim([0,20])\n", + " plt.legend()\n", + " plt.show()\n", + "\n", + "\n", + "plot_history(history)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "AqsuANc11FYv" + }, + "source": [ + "このグラフを見ると、検証エラーは100エポックを過ぎたあたりで改善が見られなくなり、むしろ悪化しているようです。検証スコアの改善が見られなくなったら自動的に訓練を停止するように、`model.fit`メソッド呼び出しを変更します。ここでは、エポック毎に訓練状態をチェックする*EarlyStopping*コールバックを使用します。設定したエポック数の間に改善が見られない場合、訓練を自動的に停止します。\n", + "\n", + "このコールバックについての詳細は[ここ](https://www.tensorflow.org/versions/master/api_docs/python/tf/keras/callbacks/EarlyStopping)を参照ください。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "fdMZuhUgzMZ4", + "colab": {} + }, + "source": [ + "model = build_model()\n", + "\n", + "# The patience parameter is the amount of epochs to check for improvement\n", + "early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=10)\n", + "\n", + "history = model.fit(normed_train_data, train_labels, epochs=EPOCHS,\n", + " validation_split = 0.2, verbose=0, callbacks=[early_stop, PrintDot()])\n", + "\n", + "plot_history(history)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "3St8-DmrX8P4" + }, + "source": [ + "検証用データセットでのグラフを見ると、平均誤差は+/- 2 MPG(マイル/ガロン)前後です。これは良い精度でしょうか?その判断はおまかせします。\n", + "\n", + "モデルの訓練に使用していない**テスト用**データセットを使って、モデルがどれくらい汎化できているか見てみましょう。これによって、モデルが実際の現場でどれくらい正確に予測できるかがわかります。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "jl_yNr5n1kms", + "colab": {} + }, + "source": [ + "loss, mae, mse = model.evaluate(normed_test_data, test_labels, verbose=0)\n", + "\n", + "print(\"Testing set Mean Abs Error: {:5.2f} MPG\".format(mae))" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "ft603OzXuEZC" + }, + "source": [ + "### モデルを使った予測\n", + "\n", + "最後に、テストデータを使ってMPG値を予測します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "Xe7RXH3N3CWU", + "colab": {} + }, + "source": [ + "test_predictions = model.predict(normed_test_data).flatten()\n", + "\n", + "plt.scatter(test_labels, test_predictions)\n", + "plt.xlabel('True Values [MPG]')\n", + "plt.ylabel('Predictions [MPG]')\n", + "plt.axis('equal')\n", + "plt.axis('square')\n", + "plt.xlim([0,plt.xlim()[1]])\n", + "plt.ylim([0,plt.ylim()[1]])\n", + "_ = plt.plot([-100, 100], [-100, 100])\n" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "OrkHGKZcusUo" + }, + "source": [ + "そこそこ良い予測ができているように見えます。誤差の分布を見てみましょう。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "f-OHX4DiXd8x", + "colab": {} + }, + "source": [ + "error = test_predictions - test_labels\n", + "plt.hist(error, bins = 25)\n", + "plt.xlabel(\"Prediction Error [MPG]\")\n", + "_ = plt.ylabel(\"Count\")" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "r9_kI6MHu1UU" + }, + "source": [ + "とても正規分布には見えませんが、サンプル数が非常に小さいからだと考えられます。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "vgGQuV-yqYZH" + }, + "source": [ + "## 結論\n", + "\n", + "このノートブックでは、回帰問題を扱うためのテクニックをいくつか紹介しました。\n", + "\n", + "* 平均二乗誤差(MSE: Mean Squared Error)は回帰問題に使われる一般的な損失関数です(分類問題には異なる損失関数が使われます)。\n", + "* 同様に、回帰問題に使われる評価指標も分類問題とは異なります。回帰問題の一般的な評価指標は平均絶対誤差(MAE: Mean Absolute Error)です。\n", + "* 入力数値特徴量の範囲が異なっている場合、特徴量毎に同じ範囲に正規化するべきです。\n", + "* 訓練用データが多くない場合、過学習を避けるために少ない隠れ層を持つ小さいネットワークを使うというのが良い方策の1つです。\n", + "* Early Stoppingは過学習を防止するための便利な手法の一つです。" + ] + } + ] +} \ No newline at end of file diff --git a/site/ja/r2/tutorials/keras/basic_text_classification.ipynb b/site/ja/r2/tutorials/keras/basic_text_classification.ipynb index 1c7b2dcff0e..61d0b6bc047 100644 --- a/site/ja/r2/tutorials/keras/basic_text_classification.ipynb +++ b/site/ja/r2/tutorials/keras/basic_text_classification.ipynb @@ -1,726 +1,736 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "Ic4_occAAiAT" - }, - "source": [ - "##### Copyright 2018 The TensorFlow Authors." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "cellView": "form", - "colab": {}, - "colab_type": "code", - "id": "ioaprt5q5US7" - }, - "outputs": [], - "source": [ - "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", - "# you may not use this file except in compliance with the License.\n", - "# You may obtain a copy of the License at\n", - "#\n", - "# https://www.apache.org/licenses/LICENSE-2.0\n", - "#\n", - "# Unless required by applicable law or agreed to in writing, software\n", - "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", - "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", - "# See the License for the specific language governing permissions and\n", - "# limitations under the License." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "cellView": "form", - "colab": {}, - "colab_type": "code", - "id": "yCl0eTNH5RS3" - }, - "outputs": [], - "source": [ - "#@title MIT License\n", - "#\n", - "# Copyright (c) 2017 François Chollet\n", - "#\n", - "# Permission is hereby granted, free of charge, to any person obtaining a\n", - "# copy of this software and associated documentation files (the \"Software\"),\n", - "# to deal in the Software without restriction, including without limitation\n", - "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n", - "# and/or sell copies of the Software, and to permit persons to whom the\n", - "# Software is furnished to do so, subject to the following conditions:\n", - "#\n", - "# The above copyright notice and this permission notice shall be included in\n", - "# all copies or substantial portions of the Software.\n", - "#\n", - "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n", - "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n", - "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n", - "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n", - "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n", - "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n", - "# DEALINGS IN THE SOFTWARE." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "ItXfxkxvosLH" - }, - "source": [ - "# 映画レビューのテキスト分類" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "hKY4XMc9o8iB" - }, - "source": [ - "\n", - " \n", - " \n", - " \n", - "
\n", - " View on TensorFlow.org\n", - " \n", - " Run in Google Colab\n", - " \n", - " View source on GitHub\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "Eg62Pmz3o83v" - }, - "source": [ - "ここでは、映画のレビューをそのテキストを使って**肯定的**か**否定的**かに分類します。これは、二値分類あるいは2クラス分類という問題の例であり、機械学習において重要でいろいろな応用が可能なものです。\n", - "\n", - "ここでは、[Internet Movie Database](https://www.imdb.com/)から抽出した50,000件の映画レビューを含む、 [IMDB dataset](https://www.tensorflow.org/api_docs/python/tf/keras/datasets/imdb) を使います。レビューは訓練用とテスト用に25,000件ずつに分割されています。訓練用とテスト用のデータは**均衡**しています。言い換えると、それぞれが同数の肯定的及び否定的なレビューを含んでいます。\n", - "\n", - "ここでは、TensorFlowを使ってモデルを構築・訓練するためのハイレベルなAPIである [tf.keras](https://www.tensorflow.org/guide/keras)を使用します。`tf.keras`を使ったもう少し高度なテキスト分類のチュートリアルについては、 [MLCC Text Classification Guide](https://developers.google.com/machine-learning/guides/text-classification/)を参照してください。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "2ew7HTbPpCJH" - }, - "outputs": [], - "source": [ - "from __future__ import absolute_import, division, print_function\n", - "\n", - "!pip install tensorflow==2.0.0-alpha0\n", - "import tensorflow as tf\n", - "from tensorflow import keras\n", - "\n", - "import numpy as np\n", - "\n", - "print(tf.__version__)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "iAsKG535pHep" - }, - "source": [ - "## IMDB datasetのダウンロード\n", - "\n", - "IMDBデータセットは、TensorFlowにパッケージ化されています。それは前処理済みのものであり、(単語の連なりである)レビューが、整数の配列に変換されています。そこでは整数が辞書中の特定の単語を表します。\n", - "\n", - "次のコードは、IMDBデータセットをあなたのパソコンにダウンロードします。(すでにダウンロードしていれば、キャッシュされたコピーを使用します)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "zXXx5Oc3pOmN" - }, - "outputs": [], - "source": [ - "imdb = keras.datasets.imdb\n", - "\n", - "(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "odr-KlzO-lkL" - }, - "source": [ - "`num_words=10000`という引数は、訓練データ中に出てくる単語のうち、最も頻繁に出現する10,000個を保持するためのものです。データサイズを管理可能にするため、稀にしか出現しない単語は破棄されます。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "l50X3GfjpU4r" - }, - "source": [ - "## データの観察\n", - "\n", - "データの形式を理解するために少し時間を割いてみましょう。このデータセットは前処理済みで、サンプルそれぞれが、映画レビューの中の単語を表す整数の配列になっています。ラベルはそれぞれ、0または1の整数値で、0が否定的レビュー、1が肯定的なレビューを示しています。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "y8qCnve_-lkO" - }, - "outputs": [], - "source": [ - "print(\"Training entries: {}, labels: {}\".format(len(train_data), len(train_labels)))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "RnKvHWW4-lkW" - }, - "source": [ - "レビューのテキストは複数の整数に変換されており、それぞれの整数が辞書の中の特定の単語を表します。最初のレビューがどのようなものか見てみましょう。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "QtTS4kpEpjbi" - }, - "outputs": [], - "source": [ - "print(train_data[0])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "hIE4l_72x7DP" - }, - "source": [ - "映画のレビューはそれぞれ長さが異なっていることでしょう。次のコードで、最初と2つ目のレビューの単語の数を見てみます。ニューラルネットワークへの入力は同じ長さでなければならないため、後ほどその問題を解決する必要があります。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "X-6Ii9Pfx6Nr" - }, - "outputs": [], - "source": [ - "len(train_data[0]), len(train_data[1])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "4wJg2FiYpuoX" - }, - "source": [ - "### 整数を単語に戻してみる\n", - "\n", - "整数をテキストに戻す方法を知っていると便利です。整数を文字列にマッピングする辞書オブジェクトを検索するためのヘルパー関数を定義します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "tr5s_1alpzop" - }, - "outputs": [], - "source": [ - "# 単語を整数にマッピングする辞書\n", - "word_index = imdb.get_word_index()\n", - "\n", - "# インデックスの最初の方は予約済み\n", - "word_index = {k:(v+3) for k,v in word_index.items()} \n", - "word_index[\"\"] = 0\n", - "word_index[\"\"] = 1\n", - "word_index[\"\"] = 2 # unknown\n", - "word_index[\"\"] = 3\n", - "\n", - "reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])\n", - "\n", - "def decode_review(text):\n", - " return ' '.join([reverse_word_index.get(i, '?') for i in text])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "U3CNRvEZVppl" - }, - "source": [ - "`decode_review`を使うと、最初のレビューのテキストを表示できます。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "s_OqxmH6-lkn" - }, - "outputs": [], - "source": [ - "decode_review(train_data[0])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "lFP_XKVRp4_S" - }, - "source": [ - "## データの準備\n", - "\n", - "レビュー(整数の配列)は、ニューラルネットワークに投入する前に、テンソルに変換する必要があります。これには2つの方法があります。\n", - "\n", - "* 配列をワンホット(one-hot)エンコーディングと同じように、単語の出現を表す0と1のベクトルに変換します。例えば、[3, 5]という配列は、インデックス3と5を除いてすべてゼロの10,000次元のベクトルになります。そして、これをネットワークの最初の層、すなわち、浮動小数点のベクトルデータを扱うことができるDense(全結合)層とします。ただし、これは単語数×レビュー数の行列が必要なメモリ集約的な方法です。\n", - "* もう一つの方法では、配列をパディングによって同じ長さに揃え、`サンプル数 * 長さの最大値`の形の整数テンソルにします。そして、この形式を扱うことができるEmbedding(埋め込み)層をネットワークの最初の層にします。\n", - "\n", - "このチュートリアルでは、後者を採用することにします。\n", - "\n", - "映画レビューは同じ長さでなければならないので、長さを標準化する [pad_sequences](https://www.tensorflow.org/versions/r1.10/api_docs/python/tf/keras/preprocessing/sequence/pad_sequences) 関数を使うことにします。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "2jQv-omsHurp" - }, - "outputs": [], - "source": [ - "train_data = keras.preprocessing.sequence.pad_sequences(train_data,\n", - " value=word_index[\"\"],\n", - " padding='post',\n", - " maxlen=256)\n", - "\n", - "test_data = keras.preprocessing.sequence.pad_sequences(test_data,\n", - " value=word_index[\"\"],\n", - " padding='post',\n", - " maxlen=256)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "VO5MBpyQdipD" - }, - "source": [ - "サンプルの長さを見てみましょう。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "USSSBnkE-lky" - }, - "outputs": [], - "source": [ - "len(train_data[0]), len(train_data[1])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "QJoxZGyfjT5V" - }, - "source": [ - "次に、パディング済みの最初のサンプルを確認します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "TG8X9cqi-lk9" - }, - "outputs": [], - "source": [ - "print(train_data[0])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "LLC02j2g-llC" - }, - "source": [ - "## モデルの構築\n", - "\n", - "ニューラルネットワークは、層を積み重ねることで構成されます。この際、2つの大きな決定が必要です。\n", - "\n", - "* モデルにいくつの**層**を設けるか?\n", - "* 層ごとに何個の**隠れユニット**を使用するか?\n", - "\n", - "この例では、入力データは単語インデックスの配列で構成されています。推定の対象となるラベルは、0または1です。この問題のためのモデルを構築しましょう。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "xpKOoWgu-llD" - }, - "outputs": [], - "source": [ - "# 入力の形式は映画レビューで使われている語彙数(10,000語)\n", - "vocab_size = 10000\n", - "\n", - "model = keras.Sequential()\n", - "model.add(keras.layers.Embedding(vocab_size, 16))\n", - "model.add(keras.layers.GlobalAveragePooling1D())\n", - "model.add(keras.layers.Dense(16, activation='relu'))\n", - "model.add(keras.layers.Dense(1, activation='sigmoid'))\n", - "\n", - "model.summary()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "6PbKQ6mucuKL" - }, - "source": [ - "これらの層は、分類器を構成するため一列に積み重ねられます。\n", - "\n", - "1. 最初の層は`Embedding`(埋め込み)層です。この層は、整数にエンコードされた語彙を受け取り、それぞれの単語インデックスに対応する埋め込みベクトルを検索します。埋め込みベクトルは、モデルの訓練の中で学習されます。ベクトル化のために、出力行列には次元が1つ追加されます。その結果、次元は、`(batch, sequence, embedding)`となります。\n", - "2. 次は、`GlobalAveragePooling1D`(1次元のグローバル平均プーリング)層です。この層は、それぞれのサンプルについて、シーケンスの次元方向に平均値をもとめ、固定長のベクトルを返します。この結果、モデルは最も単純な形で、可変長の入力を扱うことができるようになります。\n", - "3. この固定長の出力ベクトルは、16個の隠れユニットを持つ全結合(`Dense`)層に受け渡されます。\n", - "4. 最後の層は、1個の出力ノードに全結合されます。シグモイド(`sigmoid`)活性化関数を使うことで、値は確率あるいは確信度を表す0と1の間の浮動小数点数となります。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "0XMwnDOp-llH" - }, - "source": [ - "### 隠れユニット\n", - "\n", - "上記のモデルには、入力と出力の間に、2つの中間層あるいは「隠れ」層があります。出力(ユニット、ノード、またはニューロン)は、その層の内部表現の次元数です。言い換えると、このネットワークが学習によって内部表現を獲得する際の自由度ということです。\n", - "\n", - "モデルにより多くの隠れユニットがある場合(内部表現空間の次元数がより大きい場合)、または、より多くの層がある場合、あるいはその両方の場合、ネットワークはより複雑な内部表現を学習することができます。しかしながら、その結果として、ネットワークの計算量が多くなるほか、学習してほしくないパターンを学習するようになります。学習してほしくないパターンとは、訓練データでの性能は向上するものの、テスト用データの性能が向上しないパターンです。この問題を**過学習**(*overfitting*)といいます。この問題は後ほど検証することになります。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "L4EqVWg4-llM" - }, - "source": [ - "### 損失関数とオプティマイザ\n", - "\n", - "モデルを訓練するには、損失関数とオプティマイザが必要です。今回の問題は二値分類問題であり、モデルの出力は確率(1ユニットの層とシグモイド活性化関数)であるため、損失関数として`binary_crossentropy`(2値のクロスエントロピー)関数を使用することにします。\n", - "\n", - "損失関数の候補はこれだけではありません。例えば、`mean_squared_error`(平均二乗誤差)を使うこともできます。しかし、一般的には、確率を扱うには`binary_crossentropy`の方が適しています。`binary_crossentropy`は、確率分布の間の「距離」を測定する尺度です。今回の場合には、真の分布と予測値の分布の間の距離ということになります。\n", - "\n", - "後ほど、回帰問題を検証する際には(例えば家屋の値段を推定するとか)、もう一つの損失関数である`mean_squared_error`(平均二乗誤差)の使い方を目にすることになります。\n", - "\n", - "さて、モデルのオプティマイザと損失関数を設定しましょう。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "Mr0GP-cQ-llN" - }, - "outputs": [], - "source": [ - "model.compile(optimizer='adam',\n", - " loss='binary_crossentropy',\n", - " metrics=['accuracy'])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "hCWYwkug-llQ" - }, - "source": [ - "## 検証用データを作る\n", - "\n", - "訓練を行う際、モデルが見ていないデータでの正解率を検証したいと思います。もとの訓練用データから、10,000個のサンプルを取り分けて**検証用データ**(*validation set*)を作ります。(なぜ、ここでテスト用データを使わないのでしょう? 今回の目的は、訓練用データだけを使って、モデルの開発とチューニングを行うことです。その後、テスト用データを1回だけ使い、正解率を検証するのです。)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "-NpcXY9--llS" - }, - "outputs": [], - "source": [ - "x_val = train_data[:10000]\n", - "partial_x_train = train_data[10000:]\n", - "\n", - "y_val = train_labels[:10000]\n", - "partial_y_train = train_labels[10000:]" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "35jv_fzP-llU" - }, - "source": [ - "## モデルの訓練\n", - "\n", - "512個のサンプルからなるミニバッチを使って、40エポックモデルを訓練します。この結果、`x_train`と`y_train`に含まれるすべてのサンプルを40回繰り返すことになります。訓練中、検証用データの10,000サンプルを用いて、モデルの損失と正解率をモニタリングします。 " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "tXSGrjWZ-llW", - "scrolled": false - }, - "outputs": [], - "source": [ - "history = model.fit(partial_x_train,\n", - " partial_y_train,\n", - " epochs=40,\n", - " batch_size=512,\n", - " validation_data=(x_val, y_val),\n", - " verbose=1)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "9EEGuDVuzb5r" - }, - "source": [ - "## モデルの評価\n", - "\n", - "さて、モデルの性能を見てみましょう。2つの値が返されます。損失(エラーを示す数値であり、小さい方が良い)と正解率です。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "zOMKywn4zReN" - }, - "outputs": [], - "source": [ - "results = model.evaluate(test_data, test_labels)\n", - "\n", - "print(results)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "z1iEXVTR0Z2t" - }, - "source": [ - "この、かなり素朴なアプローチでも87%前後の正解率を達成しました。もっと高度なアプローチを使えば、モデルの正解率は95%に近づけることもできるでしょう。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "5KggXVeL-llZ" - }, - "source": [ - "## 正解率と損失の時系列グラフを描く\n", - "\n", - "`model.fit()` は、訓練中に発生したすべてのことを記録した辞書を含む`History` オブジェクトを返します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "VcvSXvhp-llb" - }, - "outputs": [], - "source": [ - "history_dict = history.history\n", - "history_dict.keys()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "nRKsqL40-lle" - }, - "source": [ - "4つのエントリがあります。それぞれが、訓練と検証の際にモニターしていた指標を示します。これを使って、訓練時と検証時の損失を比較するグラフと、訓練時と検証時の正解率を比較するグラフを作成することができます。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "nGoYf2Js-lle" - }, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n", - "\n", - "acc = history_dict['accuracy']\n", - "val_acc = history_dict['val_accuracy']\n", - "loss = history_dict['loss']\n", - "val_loss = history_dict['val_loss']\n", - "\n", - "epochs = range(1, len(acc) + 1)\n", - "\n", - "# \"bo\" is for \"blue dot\"\n", - "plt.plot(epochs, loss, 'bo', label='Training loss')\n", - "# b is for \"solid blue line\"\n", - "plt.plot(epochs, val_loss, 'b', label='Validation loss')\n", - "plt.title('Training and validation loss')\n", - "plt.xlabel('Epochs')\n", - "plt.ylabel('Loss')\n", - "plt.legend()\n", - "\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "6hXx-xOv-llh" - }, - "outputs": [], - "source": [ - "plt.clf() # 図のクリア\n", - "\n", - "plt.plot(epochs, acc, 'bo', label='Training acc')\n", - "plt.plot(epochs, val_acc, 'b', label='Validation acc')\n", - "plt.title('Training and validation accuracy')\n", - "plt.xlabel('Epochs')\n", - "plt.ylabel('Accuracy')\n", - "plt.legend()\n", - "\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "oFEmZ5zq-llk" - }, - "source": [ - "上記のグラフでは、点が訓練時の損失と正解率を、実線が検証時の損失と正解率を表しています。\n", - "\n", - "訓練時の損失がエポックごとに**減少**し、訓練時の正解率がエポックごとに**上昇**していることに気がつくはずです。繰り返すごとに指定された数値指標を最小化する勾配降下法を最適化に使用している場合に期待される動きです。\n", - "\n", - "これは、検証時の損失と正解率には当てはまりません。20エポックを過ぎたあたりから、横ばいになっているようです。これが、過学習の一例です。モデルの性能が、訓練用データでは高い一方で、見たことの無いデータではそれほど高くないというものです。このポイントをすぎると、モデルが最適化しすぎて、訓練用データでは特徴的であるが、テスト用データには一般化できない内部表現を学習しています。\n", - "\n", - "このケースの場合、20エポックを過ぎたあたりで訓練をやめることで、過学習を防止することが出来ます。後ほど、コールバックを使って、これを自動化する方法を紹介します。" - ] - } - ], - "metadata": { - "colab": { - "collapsed_sections": [], - "name": "basic_text_classification.ipynb", - "private_outputs": true, - "provenance": [], - "toc_visible": true, - "version": "0.3.2" - }, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "basic_text_classification.ipynb", + "version": "0.3.2", + "provenance": [], + "private_outputs": true, + "collapsed_sections": [], + "toc_visible": true + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.5" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Ic4_occAAiAT" + }, + "source": [ + "##### Copyright 2018 The TensorFlow Authors." + ] + }, + { + "cell_type": "code", + "metadata": { + "cellView": "form", + "colab_type": "code", + "id": "ioaprt5q5US7", + "colab": {} + }, + "source": [ + "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "cellView": "form", + "colab_type": "code", + "id": "yCl0eTNH5RS3", + "colab": {} + }, + "source": [ + "#@title MIT License\n", + "#\n", + "# Copyright (c) 2017 François Chollet\n", + "#\n", + "# Permission is hereby granted, free of charge, to any person obtaining a\n", + "# copy of this software and associated documentation files (the \"Software\"),\n", + "# to deal in the Software without restriction, including without limitation\n", + "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n", + "# and/or sell copies of the Software, and to permit persons to whom the\n", + "# Software is furnished to do so, subject to the following conditions:\n", + "#\n", + "# The above copyright notice and this permission notice shall be included in\n", + "# all copies or substantial portions of the Software.\n", + "#\n", + "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n", + "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n", + "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n", + "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n", + "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n", + "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n", + "# DEALINGS IN THE SOFTWARE." + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "ItXfxkxvosLH" + }, + "source": [ + "# 映画レビューのテキスト分類" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "hKY4XMc9o8iB" + }, + "source": [ + "\n", + " \n", + " \n", + " \n", + "
\n", + " View on TensorFlow.org\n", + " \n", + " Run in Google Colab\n", + " \n", + " View source on GitHub\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FKrWNcH6Xyz5", + "colab_type": "text" + }, + "source": [ + "Note: これらのドキュメントは私たちTensorFlowコミュニティが翻訳したものです。コミュニティによる 翻訳は**ベストエフォート**であるため、この翻訳が正確であることや[英語の公式ドキュメント](https://www.tensorflow.org/?hl=en)の 最新の状態を反映したものであることを保証することはできません。 この翻訳の品質を向上させるためのご意見をお持ちの方は、GitHubリポジトリ[tensorflow/docs](https://github.com/tensorflow/docs)にプルリクエストをお送りください。 コミュニティによる翻訳やレビューに参加していただける方は、 [docs-ja@tensorflow.org メーリングリスト](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ja)にご連絡ください。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Eg62Pmz3o83v" + }, + "source": [ + "ここでは、映画のレビューをそのテキストを使って**肯定的**か**否定的**かに分類します。これは、二値分類あるいは2クラス分類という問題の例であり、機械学習において重要でいろいろな応用が可能なものです。\n", + "\n", + "ここでは、[Internet Movie Database](https://www.imdb.com/)から抽出した50,000件の映画レビューを含む、 [IMDB dataset](https://www.tensorflow.org/api_docs/python/tf/keras/datasets/imdb) を使います。レビューは訓練用とテスト用に25,000件ずつに分割されています。訓練用とテスト用のデータは**均衡**しています。言い換えると、それぞれが同数の肯定的及び否定的なレビューを含んでいます。\n", + "\n", + "ここでは、TensorFlowを使ってモデルを構築・訓練するためのハイレベルなAPIである [tf.keras](https://www.tensorflow.org/guide/keras)を使用します。`tf.keras`を使ったもう少し高度なテキスト分類のチュートリアルについては、 [MLCC Text Classification Guide](https://developers.google.com/machine-learning/guides/text-classification/)を参照してください。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "2ew7HTbPpCJH", + "colab": {} + }, + "source": [ + "from __future__ import absolute_import, division, print_function\n", + "\n", + "!pip install tensorflow==2.0.0-alpha0\n", + "import tensorflow as tf\n", + "from tensorflow import keras\n", + "\n", + "import numpy as np\n", + "\n", + "print(tf.__version__)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "iAsKG535pHep" + }, + "source": [ + "## IMDB datasetのダウンロード\n", + "\n", + "IMDBデータセットは、TensorFlowにパッケージ化されています。それは前処理済みのものであり、(単語の連なりである)レビューが、整数の配列に変換されています。そこでは整数が辞書中の特定の単語を表します。\n", + "\n", + "次のコードは、IMDBデータセットをあなたのパソコンにダウンロードします。(すでにダウンロードしていれば、キャッシュされたコピーを使用します)" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "zXXx5Oc3pOmN", + "colab": {} + }, + "source": [ + "imdb = keras.datasets.imdb\n", + "\n", + "(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "odr-KlzO-lkL" + }, + "source": [ + "`num_words=10000`という引数は、訓練データ中に出てくる単語のうち、最も頻繁に出現する10,000個を保持するためのものです。データサイズを管理可能にするため、稀にしか出現しない単語は破棄されます。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "l50X3GfjpU4r" + }, + "source": [ + "## データの観察\n", + "\n", + "データの形式を理解するために少し時間を割いてみましょう。このデータセットは前処理済みで、サンプルそれぞれが、映画レビューの中の単語を表す整数の配列になっています。ラベルはそれぞれ、0または1の整数値で、0が否定的レビュー、1が肯定的なレビューを示しています。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "y8qCnve_-lkO", + "colab": {} + }, + "source": [ + "print(\"Training entries: {}, labels: {}\".format(len(train_data), len(train_labels)))" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "RnKvHWW4-lkW" + }, + "source": [ + "レビューのテキストは複数の整数に変換されており、それぞれの整数が辞書の中の特定の単語を表します。最初のレビューがどのようなものか見てみましょう。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "QtTS4kpEpjbi", + "colab": {} + }, + "source": [ + "print(train_data[0])" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "hIE4l_72x7DP" + }, + "source": [ + "映画のレビューはそれぞれ長さが異なっていることでしょう。次のコードで、最初と2つ目のレビューの単語の数を見てみます。ニューラルネットワークへの入力は同じ長さでなければならないため、後ほどその問題を解決する必要があります。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "X-6Ii9Pfx6Nr", + "colab": {} + }, + "source": [ + "len(train_data[0]), len(train_data[1])" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "4wJg2FiYpuoX" + }, + "source": [ + "### 整数を単語に戻してみる\n", + "\n", + "整数をテキストに戻す方法を知っていると便利です。整数を文字列にマッピングする辞書オブジェクトを検索するためのヘルパー関数を定義します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "tr5s_1alpzop", + "colab": {} + }, + "source": [ + "# 単語を整数にマッピングする辞書\n", + "word_index = imdb.get_word_index()\n", + "\n", + "# インデックスの最初の方は予約済み\n", + "word_index = {k:(v+3) for k,v in word_index.items()} \n", + "word_index[\"\"] = 0\n", + "word_index[\"\"] = 1\n", + "word_index[\"\"] = 2 # unknown\n", + "word_index[\"\"] = 3\n", + "\n", + "reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])\n", + "\n", + "def decode_review(text):\n", + " return ' '.join([reverse_word_index.get(i, '?') for i in text])" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "U3CNRvEZVppl" + }, + "source": [ + "`decode_review`を使うと、最初のレビューのテキストを表示できます。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "s_OqxmH6-lkn", + "colab": {} + }, + "source": [ + "decode_review(train_data[0])" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "lFP_XKVRp4_S" + }, + "source": [ + "## データの準備\n", + "\n", + "レビュー(整数の配列)は、ニューラルネットワークに投入する前に、テンソルに変換する必要があります。これには2つの方法があります。\n", + "\n", + "* 配列をワンホット(one-hot)エンコーディングと同じように、単語の出現を表す0と1のベクトルに変換します。例えば、[3, 5]という配列は、インデックス3と5を除いてすべてゼロの10,000次元のベクトルになります。そして、これをネットワークの最初の層、すなわち、浮動小数点のベクトルデータを扱うことができるDense(全結合)層とします。ただし、これは単語数×レビュー数の行列が必要なメモリ集約的な方法です。\n", + "* もう一つの方法では、配列をパディングによって同じ長さに揃え、`サンプル数 * 長さの最大値`の形の整数テンソルにします。そして、この形式を扱うことができるEmbedding(埋め込み)層をネットワークの最初の層にします。\n", + "\n", + "このチュートリアルでは、後者を採用することにします。\n", + "\n", + "映画レビューは同じ長さでなければならないので、長さを標準化する [pad_sequences](https://www.tensorflow.org/versions/r1.10/api_docs/python/tf/keras/preprocessing/sequence/pad_sequences) 関数を使うことにします。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "2jQv-omsHurp", + "colab": {} + }, + "source": [ + "train_data = keras.preprocessing.sequence.pad_sequences(train_data,\n", + " value=word_index[\"\"],\n", + " padding='post',\n", + " maxlen=256)\n", + "\n", + "test_data = keras.preprocessing.sequence.pad_sequences(test_data,\n", + " value=word_index[\"\"],\n", + " padding='post',\n", + " maxlen=256)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "VO5MBpyQdipD" + }, + "source": [ + "サンプルの長さを見てみましょう。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "USSSBnkE-lky", + "colab": {} + }, + "source": [ + "len(train_data[0]), len(train_data[1])" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "QJoxZGyfjT5V" + }, + "source": [ + "次に、パディング済みの最初のサンプルを確認します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "TG8X9cqi-lk9", + "colab": {} + }, + "source": [ + "print(train_data[0])" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "LLC02j2g-llC" + }, + "source": [ + "## モデルの構築\n", + "\n", + "ニューラルネットワークは、層を積み重ねることで構成されます。この際、2つの大きな決定が必要です。\n", + "\n", + "* モデルにいくつの**層**を設けるか?\n", + "* 層ごとに何個の**隠れユニット**を使用するか?\n", + "\n", + "この例では、入力データは単語インデックスの配列で構成されています。推定の対象となるラベルは、0または1です。この問題のためのモデルを構築しましょう。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "xpKOoWgu-llD", + "colab": {} + }, + "source": [ + "# 入力の形式は映画レビューで使われている語彙数(10,000語)\n", + "vocab_size = 10000\n", + "\n", + "model = keras.Sequential()\n", + "model.add(keras.layers.Embedding(vocab_size, 16))\n", + "model.add(keras.layers.GlobalAveragePooling1D())\n", + "model.add(keras.layers.Dense(16, activation='relu'))\n", + "model.add(keras.layers.Dense(1, activation='sigmoid'))\n", + "\n", + "model.summary()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "6PbKQ6mucuKL" + }, + "source": [ + "これらの層は、分類器を構成するため一列に積み重ねられます。\n", + "\n", + "1. 最初の層は`Embedding`(埋め込み)層です。この層は、整数にエンコードされた語彙を受け取り、それぞれの単語インデックスに対応する埋め込みベクトルを検索します。埋め込みベクトルは、モデルの訓練の中で学習されます。ベクトル化のために、出力行列には次元が1つ追加されます。その結果、次元は、`(batch, sequence, embedding)`となります。\n", + "2. 次は、`GlobalAveragePooling1D`(1次元のグローバル平均プーリング)層です。この層は、それぞれのサンプルについて、シーケンスの次元方向に平均値をもとめ、固定長のベクトルを返します。この結果、モデルは最も単純な形で、可変長の入力を扱うことができるようになります。\n", + "3. この固定長の出力ベクトルは、16個の隠れユニットを持つ全結合(`Dense`)層に受け渡されます。\n", + "4. 最後の層は、1個の出力ノードに全結合されます。シグモイド(`sigmoid`)活性化関数を使うことで、値は確率あるいは確信度を表す0と1の間の浮動小数点数となります。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "0XMwnDOp-llH" + }, + "source": [ + "### 隠れユニット\n", + "\n", + "上記のモデルには、入力と出力の間に、2つの中間層あるいは「隠れ」層があります。出力(ユニット、ノード、またはニューロン)は、その層の内部表現の次元数です。言い換えると、このネットワークが学習によって内部表現を獲得する際の自由度ということです。\n", + "\n", + "モデルにより多くの隠れユニットがある場合(内部表現空間の次元数がより大きい場合)、または、より多くの層がある場合、あるいはその両方の場合、ネットワークはより複雑な内部表現を学習することができます。しかしながら、その結果として、ネットワークの計算量が多くなるほか、学習してほしくないパターンを学習するようになります。学習してほしくないパターンとは、訓練データでの性能は向上するものの、テスト用データの性能が向上しないパターンです。この問題を**過学習**(*overfitting*)といいます。この問題は後ほど検証することになります。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "L4EqVWg4-llM" + }, + "source": [ + "### 損失関数とオプティマイザ\n", + "\n", + "モデルを訓練するには、損失関数とオプティマイザが必要です。今回の問題は二値分類問題であり、モデルの出力は確率(1ユニットの層とシグモイド活性化関数)であるため、損失関数として`binary_crossentropy`(2値のクロスエントロピー)関数を使用することにします。\n", + "\n", + "損失関数の候補はこれだけではありません。例えば、`mean_squared_error`(平均二乗誤差)を使うこともできます。しかし、一般的には、確率を扱うには`binary_crossentropy`の方が適しています。`binary_crossentropy`は、確率分布の間の「距離」を測定する尺度です。今回の場合には、真の分布と予測値の分布の間の距離ということになります。\n", + "\n", + "後ほど、回帰問題を検証する際には(例えば家屋の値段を推定するとか)、もう一つの損失関数である`mean_squared_error`(平均二乗誤差)の使い方を目にすることになります。\n", + "\n", + "さて、モデルのオプティマイザと損失関数を設定しましょう。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "Mr0GP-cQ-llN", + "colab": {} + }, + "source": [ + "model.compile(optimizer='adam',\n", + " loss='binary_crossentropy',\n", + " metrics=['accuracy'])" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "hCWYwkug-llQ" + }, + "source": [ + "## 検証用データを作る\n", + "\n", + "訓練を行う際、モデルが見ていないデータでの正解率を検証したいと思います。もとの訓練用データから、10,000個のサンプルを取り分けて**検証用データ**(*validation set*)を作ります。(なぜ、ここでテスト用データを使わないのでしょう? 今回の目的は、訓練用データだけを使って、モデルの開発とチューニングを行うことです。その後、テスト用データを1回だけ使い、正解率を検証するのです。)" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "-NpcXY9--llS", + "colab": {} + }, + "source": [ + "x_val = train_data[:10000]\n", + "partial_x_train = train_data[10000:]\n", + "\n", + "y_val = train_labels[:10000]\n", + "partial_y_train = train_labels[10000:]" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "35jv_fzP-llU" + }, + "source": [ + "## モデルの訓練\n", + "\n", + "512個のサンプルからなるミニバッチを使って、40エポックモデルを訓練します。この結果、`x_train`と`y_train`に含まれるすべてのサンプルを40回繰り返すことになります。訓練中、検証用データの10,000サンプルを用いて、モデルの損失と正解率をモニタリングします。 " + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "tXSGrjWZ-llW", + "scrolled": false, + "colab": {} + }, + "source": [ + "history = model.fit(partial_x_train,\n", + " partial_y_train,\n", + " epochs=40,\n", + " batch_size=512,\n", + " validation_data=(x_val, y_val),\n", + " verbose=1)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "9EEGuDVuzb5r" + }, + "source": [ + "## モデルの評価\n", + "\n", + "さて、モデルの性能を見てみましょう。2つの値が返されます。損失(エラーを示す数値であり、小さい方が良い)と正解率です。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "zOMKywn4zReN", + "colab": {} + }, + "source": [ + "results = model.evaluate(test_data, test_labels)\n", + "\n", + "print(results)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "z1iEXVTR0Z2t" + }, + "source": [ + "この、かなり素朴なアプローチでも87%前後の正解率を達成しました。もっと高度なアプローチを使えば、モデルの正解率は95%に近づけることもできるでしょう。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "5KggXVeL-llZ" + }, + "source": [ + "## 正解率と損失の時系列グラフを描く\n", + "\n", + "`model.fit()` は、訓練中に発生したすべてのことを記録した辞書を含む`History` オブジェクトを返します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "VcvSXvhp-llb", + "colab": {} + }, + "source": [ + "history_dict = history.history\n", + "history_dict.keys()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "nRKsqL40-lle" + }, + "source": [ + "4つのエントリがあります。それぞれが、訓練と検証の際にモニターしていた指標を示します。これを使って、訓練時と検証時の損失を比較するグラフと、訓練時と検証時の正解率を比較するグラフを作成することができます。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "nGoYf2Js-lle", + "colab": {} + }, + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "acc = history_dict['accuracy']\n", + "val_acc = history_dict['val_accuracy']\n", + "loss = history_dict['loss']\n", + "val_loss = history_dict['val_loss']\n", + "\n", + "epochs = range(1, len(acc) + 1)\n", + "\n", + "# \"bo\" is for \"blue dot\"\n", + "plt.plot(epochs, loss, 'bo', label='Training loss')\n", + "# b is for \"solid blue line\"\n", + "plt.plot(epochs, val_loss, 'b', label='Validation loss')\n", + "plt.title('Training and validation loss')\n", + "plt.xlabel('Epochs')\n", + "plt.ylabel('Loss')\n", + "plt.legend()\n", + "\n", + "plt.show()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "6hXx-xOv-llh", + "colab": {} + }, + "source": [ + "plt.clf() # 図のクリア\n", + "\n", + "plt.plot(epochs, acc, 'bo', label='Training acc')\n", + "plt.plot(epochs, val_acc, 'b', label='Validation acc')\n", + "plt.title('Training and validation accuracy')\n", + "plt.xlabel('Epochs')\n", + "plt.ylabel('Accuracy')\n", + "plt.legend()\n", + "\n", + "plt.show()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "oFEmZ5zq-llk" + }, + "source": [ + "上記のグラフでは、点が訓練時の損失と正解率を、実線が検証時の損失と正解率を表しています。\n", + "\n", + "訓練時の損失がエポックごとに**減少**し、訓練時の正解率がエポックごとに**上昇**していることに気がつくはずです。繰り返すごとに指定された数値指標を最小化する勾配降下法を最適化に使用している場合に期待される動きです。\n", + "\n", + "これは、検証時の損失と正解率には当てはまりません。20エポックを過ぎたあたりから、横ばいになっているようです。これが、過学習の一例です。モデルの性能が、訓練用データでは高い一方で、見たことの無いデータではそれほど高くないというものです。このポイントをすぎると、モデルが最適化しすぎて、訓練用データでは特徴的であるが、テスト用データには一般化できない内部表現を学習しています。\n", + "\n", + "このケースの場合、20エポックを過ぎたあたりで訓練をやめることで、過学習を防止することが出来ます。後ほど、コールバックを使って、これを自動化する方法を紹介します。" + ] + } + ] +} \ No newline at end of file diff --git a/site/ja/r2/tutorials/keras/feature_columns.ipynb b/site/ja/r2/tutorials/keras/feature_columns.ipynb index 61b3d647539..148b0538e88 100644 --- a/site/ja/r2/tutorials/keras/feature_columns.ipynb +++ b/site/ja/r2/tutorials/keras/feature_columns.ipynb @@ -1,731 +1,741 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "rNdWfPXCjTjY" - }, - "source": [ - "##### Copyright 2019 The TensorFlow Authors." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "cellView": "form", - "colab": {}, - "colab_type": "code", - "id": "I1dUQ0GejU8N" - }, - "outputs": [], - "source": [ - "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", - "# you may not use this file except in compliance with the License.\n", - "# You may obtain a copy of the License at\n", - "#\n", - "# https://www.apache.org/licenses/LICENSE-2.0\n", - "#\n", - "# Unless required by applicable law or agreed to in writing, software\n", - "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", - "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", - "# See the License for the specific language governing permissions and\n", - "# limitations under the License." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "c05P9g5WjizZ" - }, - "source": [ - "# 構造化されたデータの分類" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "zofH_gCzgplN" - }, - "source": [ - "\n", - " \n", - " \n", - " \n", - "
\n", - " \n", - " \n", - " View on TensorFlow.org\n", - " \n", - " \n", - " \n", - " Run in Google Colab\n", - " \n", - " \n", - " \n", - " View source on GitHub\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "K1y4OHpGgss7" - }, - "source": [ - "このチュートリアルでは、(例えばCSVファイルに保存された表形式データのような)構造化されたデータをどうやって分類するかを示します。ここでは、モデルの定義に[Keras](https://www.tensorflow.org/guide/keras)を、[feature columns](https://www.tensorflow.org/guide/feature_columns)をCSVファイルの列をモデルを訓練するための特徴量にマッピングするための橋渡し役として使用します。このチュートリアルには、下記のことを行うコードすべてが含まれています。\n", - "\n", - "* [Pandas](https://pandas.pydata.org/)を使用したCSVファイルの読み込み\n", - "* [tf.data](https://www.tensorflow.org/guide/datasets)を使用して行データをシャッフルし、バッチ化するための入力パイプライン構築\n", - "* feature columnsを使ったCSVの列のモデル訓練用の特徴量へのマッピング\n", - "* Kerasを使ったモデルの構築と、訓練及び評価\n", - "\n", - "## データセット\n", - "\n", - "ここでは、Cleveland Clinic Foundation for Heart Diseaseが提供している小さな[データセット](https://archive.ics.uci.edu/ml/datasets/heart+Disease)を使用します。このCSVファイルには数百行が含まれています。行が患者を、列がその属性を表します。この情報を使用して、患者が心臓疾患を持っているかを予測します。このデータセットの場合には二値分類タスクとなります。\n", - "\n", - "下記はこのデータセットの[說明](https://archive.ics.uci.edu/ml/machine-learning-databases/heart-disease/heart-disease.names)です。数値列とカテゴリー列があることに注目してください。\n", - "\n", - ">列| 說明| 特徴量の型 | データ型\n", - ">------------|--------------------|----------------------|-----------------\n", - ">Age | 年齢 | 数値型 | 整数\n", - ">Sex | (1 = 男性; 0 = 女性) | カテゴリー型 | 整数\n", - ">CP | 胸痛のタイプ (0, 1, 2, 3, 4) | カテゴリー型 | 整数\n", - ">Trestbpd | 安静時血圧 (単位:mm Hg 入院時) | 数値型 | 整数\n", - ">Chol | 血清コレステロール 単位:mg/dl | 数値型 | 整数\n", - ">FBS | (空腹時血糖 > 120 mg/dl) (1 = 真; 0 = 偽) | カテゴリー型 | 整数\n", - ">RestECG | 安静時心電図の診断結果 (0, 1, 2) | カテゴリー型 | 整数\n", - ">Thalach | 最大心拍数 | 数値型 | 整数\n", - ">Exang | 運動誘発狭心症 (1 = はい; 0 = いいえ) | カテゴリー型 | 整数\n", - ">Oldpeak | 安静時と比較した運動時のST低下 | 数値型 | 整数\n", - ">Slope | ピーク運動STセグメントの勾配 | 数値型 | 浮動小数点数\n", - ">CA | 蛍光透視法によって着色された主要血管の数(0−3) | 数値型 | 整数\n", - ">Thal | 3 = 正常; 6 = 固定欠陥; 7 = 可逆的欠陥 | カテゴリー型 | 文字列\n", - ">Target | 心臓疾患の診断 (1 = 真; 0 = 偽) | 分類 | 整数" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "VxyBFc_kKazA" - }, - "source": [ - "## TensorFlow他ライブラリのインポート" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "LuOWVJBz8a6G" - }, - "outputs": [], - "source": [ - "!pip install sklearn" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "9dEreb4QKizj" - }, - "outputs": [], - "source": [ - "from __future__ import absolute_import, division, print_function\n", - "\n", - "import numpy as np\n", - "import pandas as pd\n", - "\n", - "!pip install tensorflow==2.0.0-alpha0\n", - "import tensorflow as tf\n", - "\n", - "from tensorflow import feature_column\n", - "from tensorflow.keras import layers\n", - "from sklearn.model_selection import train_test_split" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "KCEhSZcULZ9n" - }, - "source": [ - "## Pandasを使ったデータフレーム作成\n", - "\n", - "[Pandas](https://pandas.pydata.org/)は、構造化データの読み込みや操作のための便利なユーティリティを持つPythonのライブラリです。ここでは、Pandasを使ってURLからデータをダウンロードし、データフレームに読み込みます。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "REZ57BXCLdfG" - }, - "outputs": [], - "source": [ - "URL = 'https://storage.googleapis.com/applied-dl/heart.csv'\n", - "dataframe = pd.read_csv(URL)\n", - "dataframe.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "u0zhLtQqMPem" - }, - "source": [ - "## データフレームを、訓練用、検証用、テスト用に分割\n", - "\n", - "ダウンロードしたデータセットは1つのCSVファイルです。これを、訓練用、検証用、テスト用のデータセットに分割します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "YEOpw7LhMYsI" - }, - "outputs": [], - "source": [ - "train, test = train_test_split(dataframe, test_size=0.2)\n", - "train, val = train_test_split(train, test_size=0.2)\n", - "print(len(train), 'train examples')\n", - "print(len(val), 'validation examples')\n", - "print(len(test), 'test examples')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "84ef46LXMfvu" - }, - "source": [ - "## tf.dataを使った入力パイプラインの構築\n", - "\n", - "次に、[tf.data](https://www.tensorflow.org/guide/datasets)を使ってデータフレームをラップします。こうすることで、feature columns をPandasデータフレームの列をモデル訓練用の特徴量へのマッピングするための橋渡し役として使うことができます。(メモリに収まらないぐらいの)非常に大きなCSVファイルを扱う場合には、tf.dataを使ってディスクから直接CSVファイルを読み込むことになります。この方法は、このチュートリアルでは扱いません。" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "NkcaMYP-MsRe" - }, - "outputs": [], - "source": [ - "# Pandasデータフレームからtf.dataデータセットを作るためのユーティリティメソッド\n", - "def df_to_dataset(dataframe, shuffle=True, batch_size=32):\n", - " dataframe = dataframe.copy()\n", - " labels = dataframe.pop('target')\n", - " ds = tf.data.Dataset.from_tensor_slices((dict(dataframe), labels))\n", - " if shuffle:\n", - " ds = ds.shuffle(buffer_size=len(dataframe))\n", - " ds = ds.batch(batch_size)\n", - " return ds" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "CXbbXkJvMy34" - }, - "outputs": [], - "source": [ - "batch_size = 5 # デモ用として小さなバッチサイズを使用\n", - "train_ds = df_to_dataset(train, batch_size=batch_size)\n", - "val_ds = df_to_dataset(val, shuffle=False, batch_size=batch_size)\n", - "test_ds = df_to_dataset(test, shuffle=False, batch_size=batch_size)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "qRLGSMDzM-dl" - }, - "source": [ - "## 入力パイプラインを理解する\n", - "\n", - "入力パイプラインを構築したので、それが返すデータのフォーマットを見るために呼び出してみましょう。出力を読みやすくするためにバッチサイズを小さくしてあります。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "CSBo3dUVNFc9" - }, - "outputs": [], - "source": [ - "for feature_batch, label_batch in train_ds.take(1):\n", - " print('Every feature:', list(feature_batch.keys()))\n", - " print('A batch of ages:', feature_batch['age'])\n", - " print('A batch of targets:', label_batch )" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "OT5N6Se-NQsC" - }, - "source": [ - "データセットが(データフレームにある)列名からなるディクショナリを返すことがわかります。列名から、データフレームの行に含まれる列の値が得られます。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "ttIvgLRaNoOQ" - }, - "source": [ - "## feature columnsの様々な型の例\n", - "\n", - "TensorFlowにはたくさんの型のfeature columnがあります。このセクションでは、いくつかの型のfeature columnsを作り、データフレームの列をどのように変換しているかを示します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "mxwiHFHuNhmf" - }, - "outputs": [], - "source": [ - "# いくつかの型のfeature columnsを例示するためこのバッチを使用する\n", - "example_batch = next(iter(train_ds))[0]" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "0wfLB8Q3N3UH" - }, - "outputs": [], - "source": [ - "# feature columnsを作りデータのバッチを変換する\n", - "# ユーティリティメソッド\n", - "def demo(feature_column):\n", - " feature_layer = layers.DenseFeatures(feature_column)\n", - " print(feature_layer(example_batch).numpy())" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "Q7OEKe82N-Qb" - }, - "source": [ - "### 数値コラム\n", - "\n", - "feature columnsの出力はモデルへの入力になります(上記で定義したdemo関数を使うと、データフレームの列がどのように変換されるかをつぶさに見ることができます)。[数値コラム](https://www.tensorflow.org/api_docs/python/tf/feature_column/numeric_column)は、最も単純な型のコラムです。数値コラムは実数特徴量を表現するのに使われます。このコラムを使う場合、モデルにはデータフレームの列の値がそのまま渡されます。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "QZTZ0HnHOCxC" - }, - "outputs": [], - "source": [ - "age = feature_column.numeric_column(\"age\")\n", - "demo(age)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "7a6ddSyzOKpq" - }, - "source": [ - "心臓疾患データセットでは、データフレームのほとんどの列が数値型です。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "IcSxUoYgOlA1" - }, - "source": [ - "### バケット化コラム\n", - "\n", - "数値をそのままモデルに入力するのではなく、値の範囲に基づいた異なるカテゴリーに分割したいことがあります。例えば、人の年齢を表す生データを考えてみましょう。[バケット化コラム](https://www.tensorflow.org/api_docs/python/tf/feature_column/bucketized_column)を使うと年齢を数値コラムとして表現するのではなく、年齢をいくつかのバケットに分割できます。下記のワンホット値が、各行がどの年齢範囲にあるかを表していることに注目してください。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "wJ4Wt3SAOpTQ" - }, - "outputs": [], - "source": [ - "age_buckets = feature_column.bucketized_column(age, boundaries=[18, 25, 30, 35, 40, 45, 50, 55, 60, 65])\n", - "demo(age_buckets)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "r1tArzewPb-b" - }, - "source": [ - "### カテゴリー型コラム\n", - "\n", - "このデータセットでは、Thalは('fixed'、'normal'、'reversible'のような)文字列として表現されています。文字列を直接モデルに入力することはできません。まず、文字列を数値にマッピングする必要があります。categorical vocabulary コラムを使うと、(上記で示した年齢バケットのように)文字列をワンホットベクトルとして表現することができます。カテゴリーを表す語彙(vocabulary)は[categorical_column_with_vocabulary_list](https://www.tensorflow.org/api_docs/python/tf/feature_column/categorical_column_with_vocabulary_list)を使ってリストで渡すか、[categorical_column_with_vocabulary_file](https://www.tensorflow.org/api_docs/python/tf/feature_column/categorical_column_with_vocabulary_file)を使ってファイルから読み込むことができます。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "DJ6QnSHkPtOC" - }, - "outputs": [], - "source": [ - "thal = feature_column.categorical_column_with_vocabulary_list(\n", - " 'thal', ['fixed', 'normal', 'reversible'])\n", - "\n", - "thal_one_hot = feature_column.indicator_column(thal)\n", - "demo(thal_one_hot)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "dxQloQ9jOoXL" - }, - "source": [ - "より複雑なデータセットでは、たくさんの列がカテゴリー型(例えば文字列)であることでしょう。feature columns はカテゴリー型データを扱う際に最も役に立ちます。このデータセットでは、カテゴリー型コラムは1つだけですが、他のデータセットを扱う際に使用できるいくつかの重要な型のfeature columnsを紹介するために、この列を使用することにします。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "LEFPjUr6QmwS" - }, - "source": [ - "### 埋め込み型コラム\n", - "\n", - "数種類の候補となる文字列ではなく、カテゴリー毎に数千(あるいはそれ以上)の値があるとしましょう。カテゴリーの数が多くなってくると、様々な理由から、ワンホットエンコーディングを使ってニューラルネットワークを訓練することが難しくなります。埋込み型コラムを使うと、こうした制約を克服することが可能です。[埋込み型コラム](https://www.tensorflow.org/api_docs/python/tf/feature_column/embedding_column)は、データを多次元のワンホットベクトルとして表すのではなく、セルの値が0か1かだけではなく、どんな数値でもとれるような密な低次元ベクトルとして表現します。埋め込みのサイズ(下記の例では8)は、チューニングが必要なパラメータです。\n", - "\n", - "キーポイント:カテゴリー型コラムがたくさんの選択肢を持つ場合、埋め込み型コラムを使用することが最善の方法です。ここでは例を一つ示しますので、今後様々なデータセットを扱う際には、この例を参考にしてください。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "hSlohmr2Q_UU" - }, - "outputs": [], - "source": [ - "# この埋込み型コラムの入力は、先程作成したカテゴリ型コラムであることに注意\n", - "thal_embedding = feature_column.embedding_column(thal, dimension=8)\n", - "demo(thal_embedding)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "urFCAvTVRMpB" - }, - "source": [ - "### ハッシュ化特徴コラム\n", - "\n", - "値の種類が多いカテゴリー型コラムを表現するもう一つの方法が、[categorical_column_with_hash_bucket](https://www.tensorflow.org/api_docs/python/tf/feature_column/categorical_column_with_hash_bucket)を使う方法です。このfeature columnは文字列をエンコードするために入力のハッシュ値を計算し、`hash_bucket_size`個のバケットの中から1つを選択します。このコラムを使用する場合には、語彙を用意する必要はありません。また、スペースの節約のために、実際のカテゴリー数に比べて極めて少ないバケット数を選択することも可能です。\n", - "\n", - "キーポイント:この手法の重要な欠点の一つは、異なる文字列が同じバケットにマッピングされるというハッシュ値の衝突が起きることです。実務上は、データセットによっては、この問題を無視できることがあります。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "YHU_Aj2nRRDC" - }, - "outputs": [], - "source": [ - "thal_hashed = feature_column.categorical_column_with_hash_bucket(\n", - " 'thal', hash_bucket_size=1000)\n", - "demo(feature_column.indicator_column(thal_hashed))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "fB94M27DRXtZ" - }, - "source": [ - "### クロスフィーチャーコラム\n", - "\n", - "複数の特徴量をまとめて1つの特徴量にする、[フィーチャークロス](https://developers.google.com/machine-learning/glossary/#feature_cross)として知られている手法は、モデルが特徴量の組み合わせの一つ一つに別々の重みを学習することを可能にします。ここでは年齢とThalをクロスさせて新しい特徴量を作ってみます。交差列(`crossed_column`)が、起こりうるすべての組み合わせ全体のテーブル(これは非常に大きくなる可能性があります)を作るものではないことに注意してください。クロスフィーチャーコラムは、代わりにバックエンドとしてハッシュ化コラムを使用しているため、テーブルの大きさを選択することができます。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "oaPVERd9Rep6" - }, - "outputs": [], - "source": [ - "crossed_feature = feature_column.crossed_column([age_buckets, thal], hash_bucket_size=1000)\n", - "demo(feature_column.indicator_column(crossed_feature))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "ypkI9zx6Rj1q" - }, - "source": [ - "## 使用するコラムを選択する\n", - "\n", - "これまで、いくつかのfeature columnの使い方を見てきました。いよいよモデルの訓練にそれらを使用することにします。このチュートリアルの目的は、feature columnsを使うのに必要な完全なコード(いわば力学)を示すことです。以下ではモデルを訓練するための列を適当に選びました。\n", - "\n", - "キーポイント:正確なモデルを構築するのが目的である場合には、できるだけ大きなデータセットを使用して、どの特徴量を含めるのがもっとも意味があるのかや、それらをどう表現したらよいかを、慎重に検討してください。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "4PlLY7fORuzA" - }, - "outputs": [], - "source": [ - "feature_columns = []\n", - "\n", - "# 数値コラム\n", - "for header in ['age', 'trestbps', 'chol', 'thalach', 'oldpeak', 'slope', 'ca']:\n", - " feature_columns.append(feature_column.numeric_column(header))\n", - "\n", - "# バケット化コラム\n", - "age_buckets = feature_column.bucketized_column(age, boundaries=[18, 25, 30, 35, 40, 45, 50, 55, 60, 65])\n", - "feature_columns.append(age_buckets)\n", - "\n", - "# インジケーター(カテゴリー型)コラム\n", - "thal = feature_column.categorical_column_with_vocabulary_list(\n", - " 'thal', ['fixed', 'normal', 'reversible'])\n", - "thal_one_hot = feature_column.indicator_column(thal)\n", - "feature_columns.append(thal_one_hot)\n", - "\n", - "# 埋め込み型コラム\n", - "thal_embedding = feature_column.embedding_column(thal, dimension=8)\n", - "feature_columns.append(thal_embedding)\n", - "\n", - "# クロスフィーチャーコラム\n", - "crossed_feature = feature_column.crossed_column([age_buckets, thal], hash_bucket_size=1000)\n", - "crossed_feature = feature_column.indicator_column(crossed_feature)\n", - "feature_columns.append(crossed_feature)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "M-nDp8krS_ts" - }, - "source": [ - "### 特徴量層の構築\n", - "\n", - "feature columnsを定義し終わったので、次に[DenseFeatures](https://www.tensorflow.org/versions/r2.0/api_docs/python/tf/keras/layers/DenseFeatures)層を使ってKerasモデルへの入力とします。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "6o-El1R2TGQP" - }, - "outputs": [], - "source": [ - "feature_layer = tf.keras.layers.DenseFeatures(feature_columns)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "8cf6vKfgTH0U" - }, - "source": [ - "これまでは、feature columnsの働きを見るため、小さなバッチサイズを使ってきました。ここではもう少し大きなバッチサイズの新しい入力パイプラインを作ります。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "gcemszoGSse_" - }, - "outputs": [], - "source": [ - "batch_size = 32\n", - "train_ds = df_to_dataset(train, batch_size=batch_size)\n", - "val_ds = df_to_dataset(val, shuffle=False, batch_size=batch_size)\n", - "test_ds = df_to_dataset(test, shuffle=False, batch_size=batch_size)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "bBx4Xu0eTXWq" - }, - "source": [ - "## モデルの構築、コンパイルと訓練" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "_YJPPb3xTPeZ" - }, - "outputs": [], - "source": [ - "model = tf.keras.Sequential([\n", - " feature_layer,\n", - " layers.Dense(128, activation='relu'),\n", - " layers.Dense(128, activation='relu'),\n", - " layers.Dense(1, activation='sigmoid')\n", - "])\n", - "\n", - "model.compile(optimizer='adam',\n", - " loss='binary_crossentropy',\n", - " metrics=['accuracy'])\n", - "\n", - "model.fit(train_ds, \n", - " validation_data=val_ds, \n", - " epochs=5)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "GnFmMOW0Tcaa" - }, - "outputs": [], - "source": [ - "loss, accuracy = model.evaluate(test_ds)\n", - "print(\"Accuracy\", accuracy)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "3bdfbq20V6zu" - }, - "source": [ - "キーポイント:一般的に、ディープラーニングが最良の結果となるのは、もっと大きくて、もっと複雑なデータセットです。この例のように小さなデータセットを使用する際には、強固なベースラインとして、決定木やランダムフォレストを使うことをおすすめします。このチュートリアルの目的は、訓練により正確なモデルを得ることではなく、構造化データの使い方をデモすることです。今後ご自分のデータセットに取り組まれる際の出発点として、これらのコードをお使いください。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "SotnhVWuHQCw" - }, - "source": [ - "## 次のステップ\n", - "\n", - "構造化データの分類について更に多くのことを学ぶためには、自分自身で試してみることです。別のデータセットを見つけ、上記と同様のコードを使って、それを分類するモデルを訓練してみてください。正解率を上げるためには、モデルにどの特徴量を含めたらよいかや、その特徴量をどのように表現すべきかをじっくり考えてください。" - ] - } - ], - "metadata": { - "colab": { - "collapsed_sections": [], - "name": "feature_columns.ipynb", - "private_outputs": true, - "provenance": [], - "toc_visible": true, - "version": "0.3.2" - }, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "feature_columns.ipynb", + "version": "0.3.2", + "provenance": [], + "private_outputs": true, + "collapsed_sections": [], + "toc_visible": true + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.5" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "rNdWfPXCjTjY" + }, + "source": [ + "##### Copyright 2019 The TensorFlow Authors." + ] + }, + { + "cell_type": "code", + "metadata": { + "cellView": "form", + "colab_type": "code", + "id": "I1dUQ0GejU8N", + "colab": {} + }, + "source": [ + "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "c05P9g5WjizZ" + }, + "source": [ + "# 構造化されたデータの分類" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "zofH_gCzgplN" + }, + "source": [ + "\n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " View on TensorFlow.org\n", + " \n", + " \n", + " \n", + " Run in Google Colab\n", + " \n", + " \n", + " \n", + " View source on GitHub\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lrFrIDaEZAjE", + "colab_type": "text" + }, + "source": [ + "Note: これらのドキュメントは私たちTensorFlowコミュニティが翻訳したものです。コミュニティによる 翻訳は**ベストエフォート**であるため、この翻訳が正確であることや[英語の公式ドキュメント](https://www.tensorflow.org/?hl=en)の 最新の状態を反映したものであることを保証することはできません。 この翻訳の品質を向上させるためのご意見をお持ちの方は、GitHubリポジトリ[tensorflow/docs](https://github.com/tensorflow/docs)にプルリクエストをお送りください。 コミュニティによる翻訳やレビューに参加していただける方は、 [docs-ja@tensorflow.org メーリングリスト](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ja)にご連絡ください。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "K1y4OHpGgss7" + }, + "source": [ + "このチュートリアルでは、(例えばCSVファイルに保存された表形式データのような)構造化されたデータをどうやって分類するかを示します。ここでは、モデルの定義に[Keras](https://www.tensorflow.org/guide/keras)を、[feature columns](https://www.tensorflow.org/guide/feature_columns)をCSVファイルの列をモデルを訓練するための特徴量にマッピングするための橋渡し役として使用します。このチュートリアルには、下記のことを行うコードすべてが含まれています。\n", + "\n", + "* [Pandas](https://pandas.pydata.org/)を使用したCSVファイルの読み込み\n", + "* [tf.data](https://www.tensorflow.org/guide/datasets)を使用して行データをシャッフルし、バッチ化するための入力パイプライン構築\n", + "* feature columnsを使ったCSVの列のモデル訓練用の特徴量へのマッピング\n", + "* Kerasを使ったモデルの構築と、訓練及び評価\n", + "\n", + "## データセット\n", + "\n", + "ここでは、Cleveland Clinic Foundation for Heart Diseaseが提供している小さな[データセット](https://archive.ics.uci.edu/ml/datasets/heart+Disease)を使用します。このCSVファイルには数百行が含まれています。行が患者を、列がその属性を表します。この情報を使用して、患者が心臓疾患を持っているかを予測します。このデータセットの場合には二値分類タスクとなります。\n", + "\n", + "下記はこのデータセットの[說明](https://archive.ics.uci.edu/ml/machine-learning-databases/heart-disease/heart-disease.names)です。数値列とカテゴリー列があることに注目してください。\n", + "\n", + ">列| 說明| 特徴量の型 | データ型\n", + ">------------|--------------------|----------------------|-----------------\n", + ">Age | 年齢 | 数値型 | 整数\n", + ">Sex | (1 = 男性; 0 = 女性) | カテゴリー型 | 整数\n", + ">CP | 胸痛のタイプ (0, 1, 2, 3, 4) | カテゴリー型 | 整数\n", + ">Trestbpd | 安静時血圧 (単位:mm Hg 入院時) | 数値型 | 整数\n", + ">Chol | 血清コレステロール 単位:mg/dl | 数値型 | 整数\n", + ">FBS | (空腹時血糖 > 120 mg/dl) (1 = 真; 0 = 偽) | カテゴリー型 | 整数\n", + ">RestECG | 安静時心電図の診断結果 (0, 1, 2) | カテゴリー型 | 整数\n", + ">Thalach | 最大心拍数 | 数値型 | 整数\n", + ">Exang | 運動誘発狭心症 (1 = はい; 0 = いいえ) | カテゴリー型 | 整数\n", + ">Oldpeak | 安静時と比較した運動時のST低下 | 数値型 | 整数\n", + ">Slope | ピーク運動STセグメントの勾配 | 数値型 | 浮動小数点数\n", + ">CA | 蛍光透視法によって着色された主要血管の数(0−3) | 数値型 | 整数\n", + ">Thal | 3 = 正常; 6 = 固定欠陥; 7 = 可逆的欠陥 | カテゴリー型 | 文字列\n", + ">Target | 心臓疾患の診断 (1 = 真; 0 = 偽) | 分類 | 整数" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "VxyBFc_kKazA" + }, + "source": [ + "## TensorFlow他ライブラリのインポート" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "LuOWVJBz8a6G", + "colab": {} + }, + "source": [ + "!pip install sklearn" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "9dEreb4QKizj", + "colab": {} + }, + "source": [ + "from __future__ import absolute_import, division, print_function\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "!pip install tensorflow==2.0.0-alpha0\n", + "import tensorflow as tf\n", + "\n", + "from tensorflow import feature_column\n", + "from tensorflow.keras import layers\n", + "from sklearn.model_selection import train_test_split" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "KCEhSZcULZ9n" + }, + "source": [ + "## Pandasを使ったデータフレーム作成\n", + "\n", + "[Pandas](https://pandas.pydata.org/)は、構造化データの読み込みや操作のための便利なユーティリティを持つPythonのライブラリです。ここでは、Pandasを使ってURLからデータをダウンロードし、データフレームに読み込みます。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "REZ57BXCLdfG", + "colab": {} + }, + "source": [ + "URL = 'https://storage.googleapis.com/applied-dl/heart.csv'\n", + "dataframe = pd.read_csv(URL)\n", + "dataframe.head()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "u0zhLtQqMPem" + }, + "source": [ + "## データフレームを、訓練用、検証用、テスト用に分割\n", + "\n", + "ダウンロードしたデータセットは1つのCSVファイルです。これを、訓練用、検証用、テスト用のデータセットに分割します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "YEOpw7LhMYsI", + "colab": {} + }, + "source": [ + "train, test = train_test_split(dataframe, test_size=0.2)\n", + "train, val = train_test_split(train, test_size=0.2)\n", + "print(len(train), 'train examples')\n", + "print(len(val), 'validation examples')\n", + "print(len(test), 'test examples')" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "84ef46LXMfvu" + }, + "source": [ + "## tf.dataを使った入力パイプラインの構築\n", + "\n", + "次に、[tf.data](https://www.tensorflow.org/guide/datasets)を使ってデータフレームをラップします。こうすることで、feature columns をPandasデータフレームの列をモデル訓練用の特徴量へのマッピングするための橋渡し役として使うことができます。(メモリに収まらないぐらいの)非常に大きなCSVファイルを扱う場合には、tf.dataを使ってディスクから直接CSVファイルを読み込むことになります。この方法は、このチュートリアルでは扱いません。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "NkcaMYP-MsRe", + "colab": {} + }, + "source": [ + "# Pandasデータフレームからtf.dataデータセットを作るためのユーティリティメソッド\n", + "def df_to_dataset(dataframe, shuffle=True, batch_size=32):\n", + " dataframe = dataframe.copy()\n", + " labels = dataframe.pop('target')\n", + " ds = tf.data.Dataset.from_tensor_slices((dict(dataframe), labels))\n", + " if shuffle:\n", + " ds = ds.shuffle(buffer_size=len(dataframe))\n", + " ds = ds.batch(batch_size)\n", + " return ds" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "CXbbXkJvMy34", + "colab": {} + }, + "source": [ + "batch_size = 5 # デモ用として小さなバッチサイズを使用\n", + "train_ds = df_to_dataset(train, batch_size=batch_size)\n", + "val_ds = df_to_dataset(val, shuffle=False, batch_size=batch_size)\n", + "test_ds = df_to_dataset(test, shuffle=False, batch_size=batch_size)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "qRLGSMDzM-dl" + }, + "source": [ + "## 入力パイプラインを理解する\n", + "\n", + "入力パイプラインを構築したので、それが返すデータのフォーマットを見るために呼び出してみましょう。出力を読みやすくするためにバッチサイズを小さくしてあります。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "CSBo3dUVNFc9", + "colab": {} + }, + "source": [ + "for feature_batch, label_batch in train_ds.take(1):\n", + " print('Every feature:', list(feature_batch.keys()))\n", + " print('A batch of ages:', feature_batch['age'])\n", + " print('A batch of targets:', label_batch )" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "OT5N6Se-NQsC" + }, + "source": [ + "データセットが(データフレームにある)列名からなるディクショナリを返すことがわかります。列名から、データフレームの行に含まれる列の値が得られます。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "ttIvgLRaNoOQ" + }, + "source": [ + "## feature columnsの様々な型の例\n", + "\n", + "TensorFlowにはたくさんの型のfeature columnがあります。このセクションでは、いくつかの型のfeature columnsを作り、データフレームの列をどのように変換しているかを示します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "mxwiHFHuNhmf", + "colab": {} + }, + "source": [ + "# いくつかの型のfeature columnsを例示するためこのバッチを使用する\n", + "example_batch = next(iter(train_ds))[0]" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "0wfLB8Q3N3UH", + "colab": {} + }, + "source": [ + "# feature columnsを作りデータのバッチを変換する\n", + "# ユーティリティメソッド\n", + "def demo(feature_column):\n", + " feature_layer = layers.DenseFeatures(feature_column)\n", + " print(feature_layer(example_batch).numpy())" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Q7OEKe82N-Qb" + }, + "source": [ + "### 数値コラム\n", + "\n", + "feature columnsの出力はモデルへの入力になります(上記で定義したdemo関数を使うと、データフレームの列がどのように変換されるかをつぶさに見ることができます)。[数値コラム](https://www.tensorflow.org/api_docs/python/tf/feature_column/numeric_column)は、最も単純な型のコラムです。数値コラムは実数特徴量を表現するのに使われます。このコラムを使う場合、モデルにはデータフレームの列の値がそのまま渡されます。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "QZTZ0HnHOCxC", + "colab": {} + }, + "source": [ + "age = feature_column.numeric_column(\"age\")\n", + "demo(age)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "7a6ddSyzOKpq" + }, + "source": [ + "心臓疾患データセットでは、データフレームのほとんどの列が数値型です。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "IcSxUoYgOlA1" + }, + "source": [ + "### バケット化コラム\n", + "\n", + "数値をそのままモデルに入力するのではなく、値の範囲に基づいた異なるカテゴリーに分割したいことがあります。例えば、人の年齢を表す生データを考えてみましょう。[バケット化コラム](https://www.tensorflow.org/api_docs/python/tf/feature_column/bucketized_column)を使うと年齢を数値コラムとして表現するのではなく、年齢をいくつかのバケットに分割できます。下記のワンホット値が、各行がどの年齢範囲にあるかを表していることに注目してください。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "wJ4Wt3SAOpTQ", + "colab": {} + }, + "source": [ + "age_buckets = feature_column.bucketized_column(age, boundaries=[18, 25, 30, 35, 40, 45, 50, 55, 60, 65])\n", + "demo(age_buckets)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "r1tArzewPb-b" + }, + "source": [ + "### カテゴリー型コラム\n", + "\n", + "このデータセットでは、Thalは('fixed'、'normal'、'reversible'のような)文字列として表現されています。文字列を直接モデルに入力することはできません。まず、文字列を数値にマッピングする必要があります。categorical vocabulary コラムを使うと、(上記で示した年齢バケットのように)文字列をワンホットベクトルとして表現することができます。カテゴリーを表す語彙(vocabulary)は[categorical_column_with_vocabulary_list](https://www.tensorflow.org/api_docs/python/tf/feature_column/categorical_column_with_vocabulary_list)を使ってリストで渡すか、[categorical_column_with_vocabulary_file](https://www.tensorflow.org/api_docs/python/tf/feature_column/categorical_column_with_vocabulary_file)を使ってファイルから読み込むことができます。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "DJ6QnSHkPtOC", + "colab": {} + }, + "source": [ + "thal = feature_column.categorical_column_with_vocabulary_list(\n", + " 'thal', ['fixed', 'normal', 'reversible'])\n", + "\n", + "thal_one_hot = feature_column.indicator_column(thal)\n", + "demo(thal_one_hot)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "dxQloQ9jOoXL" + }, + "source": [ + "より複雑なデータセットでは、たくさんの列がカテゴリー型(例えば文字列)であることでしょう。feature columns はカテゴリー型データを扱う際に最も役に立ちます。このデータセットでは、カテゴリー型コラムは1つだけですが、他のデータセットを扱う際に使用できるいくつかの重要な型のfeature columnsを紹介するために、この列を使用することにします。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "LEFPjUr6QmwS" + }, + "source": [ + "### 埋め込み型コラム\n", + "\n", + "数種類の候補となる文字列ではなく、カテゴリー毎に数千(あるいはそれ以上)の値があるとしましょう。カテゴリーの数が多くなってくると、様々な理由から、ワンホットエンコーディングを使ってニューラルネットワークを訓練することが難しくなります。埋込み型コラムを使うと、こうした制約を克服することが可能です。[埋込み型コラム](https://www.tensorflow.org/api_docs/python/tf/feature_column/embedding_column)は、データを多次元のワンホットベクトルとして表すのではなく、セルの値が0か1かだけではなく、どんな数値でもとれるような密な低次元ベクトルとして表現します。埋め込みのサイズ(下記の例では8)は、チューニングが必要なパラメータです。\n", + "\n", + "キーポイント:カテゴリー型コラムがたくさんの選択肢を持つ場合、埋め込み型コラムを使用することが最善の方法です。ここでは例を一つ示しますので、今後様々なデータセットを扱う際には、この例を参考にしてください。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "hSlohmr2Q_UU", + "colab": {} + }, + "source": [ + "# この埋込み型コラムの入力は、先程作成したカテゴリ型コラムであることに注意\n", + "thal_embedding = feature_column.embedding_column(thal, dimension=8)\n", + "demo(thal_embedding)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "urFCAvTVRMpB" + }, + "source": [ + "### ハッシュ化特徴コラム\n", + "\n", + "値の種類が多いカテゴリー型コラムを表現するもう一つの方法が、[categorical_column_with_hash_bucket](https://www.tensorflow.org/api_docs/python/tf/feature_column/categorical_column_with_hash_bucket)を使う方法です。このfeature columnは文字列をエンコードするために入力のハッシュ値を計算し、`hash_bucket_size`個のバケットの中から1つを選択します。このコラムを使用する場合には、語彙を用意する必要はありません。また、スペースの節約のために、実際のカテゴリー数に比べて極めて少ないバケット数を選択することも可能です。\n", + "\n", + "キーポイント:この手法の重要な欠点の一つは、異なる文字列が同じバケットにマッピングされるというハッシュ値の衝突が起きることです。実務上は、データセットによっては、この問題を無視できることがあります。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "YHU_Aj2nRRDC", + "colab": {} + }, + "source": [ + "thal_hashed = feature_column.categorical_column_with_hash_bucket(\n", + " 'thal', hash_bucket_size=1000)\n", + "demo(feature_column.indicator_column(thal_hashed))" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "fB94M27DRXtZ" + }, + "source": [ + "### クロスフィーチャーコラム\n", + "\n", + "複数の特徴量をまとめて1つの特徴量にする、[フィーチャークロス](https://developers.google.com/machine-learning/glossary/#feature_cross)として知られている手法は、モデルが特徴量の組み合わせの一つ一つに別々の重みを学習することを可能にします。ここでは年齢とThalをクロスさせて新しい特徴量を作ってみます。交差列(`crossed_column`)が、起こりうるすべての組み合わせ全体のテーブル(これは非常に大きくなる可能性があります)を作るものではないことに注意してください。クロスフィーチャーコラムは、代わりにバックエンドとしてハッシュ化コラムを使用しているため、テーブルの大きさを選択することができます。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "oaPVERd9Rep6", + "colab": {} + }, + "source": [ + "crossed_feature = feature_column.crossed_column([age_buckets, thal], hash_bucket_size=1000)\n", + "demo(feature_column.indicator_column(crossed_feature))" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "ypkI9zx6Rj1q" + }, + "source": [ + "## 使用するコラムを選択する\n", + "\n", + "これまで、いくつかのfeature columnの使い方を見てきました。いよいよモデルの訓練にそれらを使用することにします。このチュートリアルの目的は、feature columnsを使うのに必要な完全なコード(いわば力学)を示すことです。以下ではモデルを訓練するための列を適当に選びました。\n", + "\n", + "キーポイント:正確なモデルを構築するのが目的である場合には、できるだけ大きなデータセットを使用して、どの特徴量を含めるのがもっとも意味があるのかや、それらをどう表現したらよいかを、慎重に検討してください。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "4PlLY7fORuzA", + "colab": {} + }, + "source": [ + "feature_columns = []\n", + "\n", + "# 数値コラム\n", + "for header in ['age', 'trestbps', 'chol', 'thalach', 'oldpeak', 'slope', 'ca']:\n", + " feature_columns.append(feature_column.numeric_column(header))\n", + "\n", + "# バケット化コラム\n", + "age_buckets = feature_column.bucketized_column(age, boundaries=[18, 25, 30, 35, 40, 45, 50, 55, 60, 65])\n", + "feature_columns.append(age_buckets)\n", + "\n", + "# インジケーター(カテゴリー型)コラム\n", + "thal = feature_column.categorical_column_with_vocabulary_list(\n", + " 'thal', ['fixed', 'normal', 'reversible'])\n", + "thal_one_hot = feature_column.indicator_column(thal)\n", + "feature_columns.append(thal_one_hot)\n", + "\n", + "# 埋め込み型コラム\n", + "thal_embedding = feature_column.embedding_column(thal, dimension=8)\n", + "feature_columns.append(thal_embedding)\n", + "\n", + "# クロスフィーチャーコラム\n", + "crossed_feature = feature_column.crossed_column([age_buckets, thal], hash_bucket_size=1000)\n", + "crossed_feature = feature_column.indicator_column(crossed_feature)\n", + "feature_columns.append(crossed_feature)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "M-nDp8krS_ts" + }, + "source": [ + "### 特徴量層の構築\n", + "\n", + "feature columnsを定義し終わったので、次に[DenseFeatures](https://www.tensorflow.org/versions/r2.0/api_docs/python/tf/keras/layers/DenseFeatures)層を使ってKerasモデルへの入力とします。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "6o-El1R2TGQP", + "colab": {} + }, + "source": [ + "feature_layer = tf.keras.layers.DenseFeatures(feature_columns)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "8cf6vKfgTH0U" + }, + "source": [ + "これまでは、feature columnsの働きを見るため、小さなバッチサイズを使ってきました。ここではもう少し大きなバッチサイズの新しい入力パイプラインを作ります。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "gcemszoGSse_", + "colab": {} + }, + "source": [ + "batch_size = 32\n", + "train_ds = df_to_dataset(train, batch_size=batch_size)\n", + "val_ds = df_to_dataset(val, shuffle=False, batch_size=batch_size)\n", + "test_ds = df_to_dataset(test, shuffle=False, batch_size=batch_size)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "bBx4Xu0eTXWq" + }, + "source": [ + "## モデルの構築、コンパイルと訓練" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "_YJPPb3xTPeZ", + "colab": {} + }, + "source": [ + "model = tf.keras.Sequential([\n", + " feature_layer,\n", + " layers.Dense(128, activation='relu'),\n", + " layers.Dense(128, activation='relu'),\n", + " layers.Dense(1, activation='sigmoid')\n", + "])\n", + "\n", + "model.compile(optimizer='adam',\n", + " loss='binary_crossentropy',\n", + " metrics=['accuracy'])\n", + "\n", + "model.fit(train_ds, \n", + " validation_data=val_ds, \n", + " epochs=5)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "GnFmMOW0Tcaa", + "colab": {} + }, + "source": [ + "loss, accuracy = model.evaluate(test_ds)\n", + "print(\"Accuracy\", accuracy)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "3bdfbq20V6zu" + }, + "source": [ + "キーポイント:一般的に、ディープラーニングが最良の結果となるのは、もっと大きくて、もっと複雑なデータセットです。この例のように小さなデータセットを使用する際には、強固なベースラインとして、決定木やランダムフォレストを使うことをおすすめします。このチュートリアルの目的は、訓練により正確なモデルを得ることではなく、構造化データの使い方をデモすることです。今後ご自分のデータセットに取り組まれる際の出発点として、これらのコードをお使いください。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "SotnhVWuHQCw" + }, + "source": [ + "## 次のステップ\n", + "\n", + "構造化データの分類について更に多くのことを学ぶためには、自分自身で試してみることです。別のデータセットを見つけ、上記と同様のコードを使って、それを分類するモデルを訓練してみてください。正解率を上げるためには、モデルにどの特徴量を含めたらよいかや、その特徴量をどのように表現すべきかをじっくり考えてください。" + ] + } + ] +} \ No newline at end of file diff --git a/site/ja/r2/tutorials/keras/index.md b/site/ja/r2/tutorials/keras/index.md index baa4fbb86a5..5c6a85b25c9 100644 --- a/site/ja/r2/tutorials/keras/index.md +++ b/site/ja/r2/tutorials/keras/index.md @@ -1,22 +1,23 @@ # 機械学習の学習と実践 -この一連のノートブックは、書籍 *[Deep Learning with Python](https://books.google.com/books?id=Yo3CAQAACAAJ)* -(邦訳「[PythonとKerasによるディープラーニング](https://book.mynavi.jp/ec/products/detail/id=90124) 」)に触発された -ものです。 -これらのチュートリアルでは、TensorFlowでディープラーニングモデルの -構築と訓練を行うためのハイレベルなPython APIである`tf.keras`を -使用しています。KerasをTensorFlowとともに使う方法の詳細は、 -[TensorFlow Keras Guide](../../guide/keras)を参照してください。 +この一連のノートブックは、書籍 +*[Deep Learning with Python](https://books.google.com/books?id=Yo3CAQAACAAJ)* +(邦訳 +「[PythonとKerasによるディープラーニング](https://book.mynavi.jp/ec/products/detail/id=90124)」) +に触発された ものです。 これらのチュートリアルでは、TensorFlowでディープラーニングモデルの 構築と訓練を行うためのハイレベルなPython +APIである`tf.keras`を 使用しています。KerasをTensorFlowとともに使う方法の詳細は、 +[TensorFlow Keras Guide](../../guide/keras.ipynb)を参照してください。 出版社から:*Deep Learning with Python*では、Python言語と強力なKeras ライブラリを使ってディープラーニングを紹介しています。 著者はKerasの作者でGoogleのAI研究者でもあるFrançois Cholletです。 この本では、直感的な説明と実践的な例を通して理解を深めることができます。 -機械学習の基礎と概念を学ぶには、[Machine Learning Crash Course](https://developers.google.com/machine-learning/crash-course/)をおすすめします。TensorFlowと機械学習については[next steps](../next_steps)に更に掲載されています。 +機械学習の基礎と概念を学ぶには、[Machine Learning Crash Course](https://developers.google.com/machine-learning/crash-course/)をおすすめします。 1. [分類問題の基本](./basic_classification.ipynb) 2. [テキスト分類](./basic_text_classification.ipynb) -3. [回帰](./basic_regression.ipynb) -4. [過学習と学習不足](./overfit_and_underfit.ipynb) -5. [モデルの保存と復元](./save_and_restore_models.ipynb) +3. [Classify structured data](./feature_columns.ipynb) +4. [回帰](./basic_regression.ipynb) +5. [過学習と学習不足](./overfit_and_underfit.ipynb) +6. [モデルの保存と復元](./save_and_restore_models.ipynb) diff --git a/site/ja/r2/tutorials/keras/overfit_and_underfit.ipynb b/site/ja/r2/tutorials/keras/overfit_and_underfit.ipynb index ea3560f4367..bc57d21cde4 100644 --- a/site/ja/r2/tutorials/keras/overfit_and_underfit.ipynb +++ b/site/ja/r2/tutorials/keras/overfit_and_underfit.ipynb @@ -1,724 +1,705 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "fTFj8ft5dlbS" - }, - "source": [ - "##### Copyright 2018 The TensorFlow Authors." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "cellView": "form", - "colab": {}, - "colab_type": "code", - "id": "lzyBOpYMdp3F" - }, - "outputs": [], - "source": [ - "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", - "# you may not use this file except in compliance with the License.\n", - "# You may obtain a copy of the License at\n", - "#\n", - "# https://www.apache.org/licenses/LICENSE-2.0\n", - "#\n", - "# Unless required by applicable law or agreed to in writing, software\n", - "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", - "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", - "# See the License for the specific language governing permissions and\n", - "# limitations under the License." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "cellView": "form", - "colab": {}, - "colab_type": "code", - "id": "m_x4KfSJ7Vt7" - }, - "outputs": [], - "source": [ - "#@title MIT License\n", - "#\n", - "# Copyright (c) 2017 François Chollet\n", - "#\n", - "# Permission is hereby granted, free of charge, to any person obtaining a\n", - "# copy of this software and associated documentation files (the \"Software\"),\n", - "# to deal in the Software without restriction, including without limitation\n", - "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n", - "# and/or sell copies of the Software, and to permit persons to whom the\n", - "# Software is furnished to do so, subject to the following conditions:\n", - "#\n", - "# The above copyright notice and this permission notice shall be included in\n", - "# all copies or substantial portions of the Software.\n", - "#\n", - "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n", - "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n", - "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n", - "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n", - "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n", - "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n", - "# DEALINGS IN THE SOFTWARE." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "C9HmC2T4ld5B" - }, - "source": [ - "# 過学習と学習不足について知る" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "kRTxFhXAlnl1" - }, - "source": [ - "\n", - " \n", - " \n", - " \n", - "
\n", - " View on TensorFlow.org\n", - " \n", - " Run in Google Colab\n", - " \n", - " View source on GitHub\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "19rPukKZsPG6" - }, - "source": [ - "いつものように、この例のプログラムは`tf.keras` APIを使用します。詳しくはTensorFlowの[Keras guide](https://www.tensorflow.org/guide/keras)を参照してください。\n", - "\n", - "これまでの例、つまり、映画レビューの分類と燃費の推定では、検証用データでのモデルの正解率が、数エポックでピークを迎え、その後低下するという現象が見られました。\n", - "\n", - "言い換えると、モデルが訓練用データを**過学習**したと考えられます。過学習への対処の仕方を学ぶことは重要です。**訓練用データセット**で高い正解率を達成することは難しくありませんが、我々は、(これまで見たこともない)**テスト用データ**に汎化したモデルを開発したいのです。\n", - "\n", - "過学習の反対語は**学習不足**(underfitting)です。学習不足は、モデルがテストデータに対してまだ改善の余地がある場合に発生します。学習不足の原因は様々です。モデルが十分強力でないとか、正則化のしすぎだとか、単に訓練時間が短すぎるといった理由があります。学習不足は、訓練用データの中の関連したパターンを学習しきっていないということを意味します。\n", - "\n", - "モデルの訓練をやりすぎると、モデルは過学習を始め、訓練用データの中のパターンで、テストデータには一般的ではないパターンを学習します。我々は、過学習と学習不足の中間を目指す必要があります。これから見ていくように、ちょうどよいエポック数だけ訓練を行うというのは必要なスキルなのです。\n", - "\n", - "過学習を防止するための、最良の解決策は、より多くの訓練用データを使うことです。多くのデータで訓練を行えば行うほど、モデルは自然により汎化していく様になります。これが不可能な場合、次善の策は正則化のようなテクニックを使うことです。正則化は、モデルに保存される情報の量とタイプに制約を課すものです。ネットワークが少数のパターンしか記憶できなければ、最適化プロセスにより、最も主要なパターンのみを学習することになり、より汎化される可能性が高くなります。\n", - "\n", - "このノートブックでは、重みの正則化とドロップアウトという、よく使われる2つの正則化テクニックをご紹介します。これらを使って、IMDBの映画レビューを分類するノートブックの改善を図ります。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "5pZ8A2liqvgk" - }, - "outputs": [], - "source": [ - "from __future__ import absolute_import, division, print_function\n", - "\n", - "!pip install tensorflow==2.0.0-alpha0\n", - "import tensorflow as tf\n", - "from tensorflow import keras\n", - "\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "\n", - "print(tf.__version__)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "1cweoTiruj8O" - }, - "source": [ - "## IMDBデータセットのダウンロード\n", - "\n", - "以前のノートブックで使用したエンベディングの代わりに、ここでは文をマルチホットエンコードします。このモデルは、訓練用データセットをすぐに過学習します。このモデルを使って、過学習がいつ起きるかということと、どうやって過学習と戦うかをデモします。\n", - "\n", - "リストをマルチホットエンコードすると言うのは、0と1のベクトルにするということです。具体的にいうと、例えば`[3, 5]`というシーケンスを、インデックス3と5の値が1で、それ以外がすべて0の、10,000次元のベクトルに変換するということを意味します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "QpzE4iqZtJly" - }, - "outputs": [], - "source": [ - "NUM_WORDS = 10000\n", - "\n", - "(train_data, train_labels), (test_data, test_labels) = keras.datasets.imdb.load_data(num_words=NUM_WORDS)\n", - "\n", - "def multi_hot_sequences(sequences, dimension):\n", - " # 形状が (len(sequences), dimension)ですべて0の行列を作る\n", - " results = np.zeros((len(sequences), dimension))\n", - " for i, word_indices in enumerate(sequences):\n", - " results[i, word_indices] = 1.0 # 特定のインデックスに対してresults[i] を1に設定する\n", - " return results\n", - "\n", - "\n", - "train_data = multi_hot_sequences(train_data, dimension=NUM_WORDS)\n", - "test_data = multi_hot_sequences(test_data, dimension=NUM_WORDS)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "MzWVeXe3NBTn" - }, - "source": [ - "結果として得られるマルチホットベクトルの1つを見てみましょう。単語のインデックスは頻度順にソートされています。このため、インデックスが0に近いほど1が多く出現するはずです。分布を見てみましょう。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "71kr5rG4LkGM" - }, - "outputs": [], - "source": [ - "plt.plot(train_data[0])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "lglk41MwvU5o" - }, - "source": [ - "## 過学習のデモ\n", - "\n", - "過学習を防止するための最も単純な方法は、モデルのサイズ、すなわち、モデル内の学習可能なパラメータの数を小さくすることです(学習パラメータの数は、層の数と層ごとのユニット数で決まります)。ディープラーニングでは、モデルの学習可能なパラメータ数を、しばしばモデルの「キャパシティ」と呼びます。直感的に考えれば、パラメータ数の多いモデルほど「記憶容量」が大きくなり、訓練用のサンプルとその目的変数の間の辞書のようなマッピングをたやすく学習することができます。このマッピングには汎化能力がまったくなく、これまで見たことが無いデータを使って予測をする際には役に立ちません。\n", - "\n", - "ディープラーニングのモデルは訓練用データに適応しやすいけれど、本当のチャレレンジは汎化であって適応ではないということを、肝に銘じておく必要があります。\n", - "\n", - "一方、ネットワークの記憶容量が限られている場合、前述のようなマッピングを簡単に学習することはできません。損失を減らすためには、より予測能力が高い圧縮された表現を学習しなければなりません。同時に、モデルを小さくしすぎると、訓練用データに適応するのが難しくなります。「多すぎる容量」と「容量不足」の間にちょうどよい容量があるのです。\n", - "\n", - "残念ながら、(層の数や、層ごとの大きさといった)モデルの適切なサイズやアーキテクチャを決める魔法の方程式はありません。一連の異なるアーキテクチャを使って実験を行う必要があります。\n", - "\n", - "適切なモデルのサイズを見つけるには、比較的少ない層の数とパラメータから始めるのがベストです。それから、検証用データでの損失値の改善が見られなくなるまで、徐々に層の大きさを増やしたり、新たな層を加えたりします。映画レビューの分類ネットワークでこれを試してみましょう。\n", - "\n", - "比較基準として、```Dense```層だけを使ったシンプルなモデルを構築し、その後、それより小さいバージョンと大きいバージョンを作って比較します。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "_ReKHdC2EgVu" - }, - "source": [ - "### 比較基準を作る" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "QKgdXPx9usBa" - }, - "outputs": [], - "source": [ - "baseline_model = keras.Sequential([\n", - " # `.summary` を見るために`input_shape`が必要 \n", - " keras.layers.Dense(16, activation='relu', input_shape=(NUM_WORDS,)),\n", - " keras.layers.Dense(16, activation='relu'),\n", - " keras.layers.Dense(1, activation='sigmoid')\n", - "])\n", - "\n", - "baseline_model.compile(optimizer='adam',\n", - " loss='binary_crossentropy',\n", - " metrics=['accuracy', 'binary_crossentropy'])\n", - "\n", - "baseline_model.summary()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "LqG3MXF5xSjR" - }, - "outputs": [], - "source": [ - "baseline_history = baseline_model.fit(train_data,\n", - " train_labels,\n", - " epochs=20,\n", - " batch_size=512,\n", - " validation_data=(test_data, test_labels),\n", - " verbose=2)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "L-DGRBbGxI6G" - }, - "source": [ - "### より小さいモデルの構築" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "SrfoVQheYSO5" - }, - "source": [ - "今作成したばかりの比較基準となるモデルに比べて隠れユニット数が少ないモデルを作りましょう。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "jksi-XtaxDAh" - }, - "outputs": [], - "source": [ - "smaller_model = keras.Sequential([\n", - " keras.layers.Dense(4, activation='relu', input_shape=(NUM_WORDS,)),\n", - " keras.layers.Dense(4, activation='relu'),\n", - " keras.layers.Dense(1, activation='sigmoid')\n", - "])\n", - "\n", - "smaller_model.compile(optimizer='adam',\n", - " loss='binary_crossentropy',\n", - " metrics=['accuracy', 'binary_crossentropy'])\n", - "\n", - "smaller_model.summary()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "jbngCZliYdma" - }, - "source": [ - "同じデータを使って訓練します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "Ofn1AwDhx-Fe" - }, - "outputs": [], - "source": [ - "smaller_history = smaller_model.fit(train_data,\n", - " train_labels,\n", - " epochs=20,\n", - " batch_size=512,\n", - " validation_data=(test_data, test_labels),\n", - " verbose=2)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "vIPuf23FFaVn" - }, - "source": [ - "### より大きなモデルの構築\n", - "\n", - "練習として、より大きなモデルを作成し、どれほど急速に過学習が起きるかを見ることもできます。次はこのベンチマークに、この問題が必要とするよりはるかに容量の大きなネットワークを追加しましょう。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "ghQwwqwqvQM9" - }, - "outputs": [], - "source": [ - "bigger_model = keras.models.Sequential([\n", - " keras.layers.Dense(512, activation='relu', input_shape=(NUM_WORDS,)),\n", - " keras.layers.Dense(512, activation='relu'),\n", - " keras.layers.Dense(1, activation='sigmoid')\n", - "])\n", - "\n", - "bigger_model.compile(optimizer='adam',\n", - " loss='binary_crossentropy',\n", - " metrics=['accuracy','binary_crossentropy'])\n", - "\n", - "bigger_model.summary()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "D-d-i5DaYmr7" - }, - "source": [ - "このモデルもまた同じデータを使って訓練します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "U1A99dhqvepf" - }, - "outputs": [], - "source": [ - "bigger_history = bigger_model.fit(train_data, train_labels,\n", - " epochs=20,\n", - " batch_size=512,\n", - " validation_data=(test_data, test_labels),\n", - " verbose=2)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "Fy3CMUZpzH3d" - }, - "source": [ - "### 訓練時と検証時の損失をグラフにする\n", - "\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "HSlo1F4xHuuM" - }, - "source": [ - "実線は訓練用データセットの損失、破線は検証用データセットでの損失です(検証用データでの損失が小さい方が良いモデルです)。これをみると、小さいネットワークのほうが比較基準のモデルよりも過学習が始まるのが遅いことがわかります(4エポックではなく6エポック後)。また、過学習が始まっても性能の低下がよりゆっくりしています。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "0XmKDtOWzOpk" - }, - "outputs": [], - "source": [ - "def plot_history(histories, key='binary_crossentropy'):\n", - " plt.figure(figsize=(16,10))\n", - " \n", - " for name, history in histories:\n", - " val = plt.plot(history.epoch, history.history['val_'+key],\n", - " '--', label=name.title()+' Val')\n", - " plt.plot(history.epoch, history.history[key], color=val[0].get_color(),\n", - " label=name.title()+' Train')\n", - "\n", - " plt.xlabel('Epochs')\n", - " plt.ylabel(key.replace('_',' ').title())\n", - " plt.legend()\n", - "\n", - " plt.xlim([0,max(history.epoch)])\n", - "\n", - "\n", - "plot_history([('baseline', baseline_history),\n", - " ('smaller', smaller_history),\n", - " ('bigger', bigger_history)])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "Bi6hBhdnSfjA" - }, - "source": [ - "より大きなネットワークでは、すぐに、1エポックで過学習が始まり、その度合も強いことに注目してください。ネットワークの容量が大きいほど訓練用データをモデル化するスピードが早くなり(結果として訓練時の損失値が小さくなり)ますが、より過学習しやすく(結果として訓練時の損失値と検証時の損失値が大きく乖離しやすく)なります。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "ASdv7nsgEFhx" - }, - "source": [ - "## 過学習防止の戦略" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "4rHoVWcswFLa" - }, - "source": [ - "### 重みの正則化を加える\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "kRxWepNawbBK" - }, - "source": [ - "「オッカムの剃刀」の原則をご存知でしょうか。何かの説明が2つあるとすると、最も正しいと考えられる説明は、仮定の数が最も少ない「一番単純な」説明だというものです。この原則は、ニューラルネットワークを使って学習されたモデルにも当てはまります。ある訓練用データとネットワーク構造があって、そのデータを説明できる重みの集合が複数ある時(つまり、複数のモデルがある時)、単純なモデルのほうが複雑なものよりも過学習しにくいのです。\n", - "\n", - "ここで言う「単純なモデル」とは、パラメータ値の分布のエントロピーが小さいもの(あるいは、上記で見たように、そもそもパラメータの数が少ないもの)です。したがって、過学習を緩和するための一般的な手法は、重みが小さい値のみをとることで、重み値の分布がより整然となる(正則)様に制約を与えるものです。これを「重みの正則化」と呼ばれ、ネットワークの損失関数に、重みの大きさに関連するコストを加えることで行われます。このコストには2つの種類があります。\n", - "\n", - "* [L1正則化](https://developers.google.com/machine-learning/glossary/#L1_regularization) 重み係数の絶対値に比例するコストを加える(重みの「L1ノルム」と呼ばれる)。\n", - "\n", - "* [L2正則化](https://developers.google.com/machine-learning/glossary/#L2_regularization) 重み係数の二乗に比例するコストを加える(重み係数の二乗「L2ノルム」と呼ばれる)。L2正則化はニューラルネットワーク用語では重み減衰(Weight Decay)と呼ばれる。呼び方が違うので混乱しないように。重み減衰は数学的にはL2正則化と同義である。\n", - "\n", - "L1正則化は重みパラメータの一部を0にすることでモデルを疎にする効果があります。L2正則化は重みパラメータにペナルティを加えますがモデルを疎にすることはありません。これは、L2正則化のほうが一般的である理由の一つです。\n", - "\n", - "`tf.keras`では、重みの正則化をするために、重み正則化のインスタンスをキーワード引数として層に加えます。ここでは、L2正則化を追加してみましょう。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "HFGmcwduwVyQ" - }, - "outputs": [], - "source": [ - "l2_model = keras.models.Sequential([\n", - " keras.layers.Dense(16, kernel_regularizer=keras.regularizers.l2(0.001),\n", - " activation='relu', input_shape=(NUM_WORDS,)),\n", - " keras.layers.Dense(16, kernel_regularizer=keras.regularizers.l2(0.001),\n", - " activation='relu'),\n", - " keras.layers.Dense(1, activation='sigmoid')\n", - "])\n", - "\n", - "l2_model.compile(optimizer='adam',\n", - " loss='binary_crossentropy',\n", - " metrics=['accuracy', 'binary_crossentropy'])\n", - "\n", - "l2_model_history = l2_model.fit(train_data, train_labels,\n", - " epochs=20,\n", - " batch_size=512,\n", - " validation_data=(test_data, test_labels),\n", - " verbose=2)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "bUUHoXb7w-_C" - }, - "source": [ - "```l2(0.001)```というのは、層の重み行列の係数全てに対して```0.001 * 重み係数の値 **2```をネットワークの損失値合計に加えることを意味します。このペナルティは訓練時のみに加えられるため、このネットワークの損失値は、訓練時にはテスト時に比べて大きくなることに注意してください。\n", - "\n", - "L2正則化の影響を見てみましょう。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "7wkfLyxBZdh_" - }, - "outputs": [], - "source": [ - "plot_history([('baseline', baseline_history),\n", - " ('l2', l2_model_history)])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "Kx1YHMsVxWjP" - }, - "source": [ - "ご覧のように、L2正則化ありのモデルは比較基準のモデルに比べて過学習しにくくなっています。両方のモデルのパラメータ数は同じであるにもかかわらずです。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "HmnBNOOVxiG8" - }, - "source": [ - "### ドロップアウトを追加する\n", - "\n", - "ドロップアウトは、ニューラルネットワークの正則化テクニックとして最もよく使われる手法の一つです。この手法は、トロント大学のヒントンと彼の学生が開発したものです。ドロップアウトは層に適用するもので、訓練時に層から出力された特徴量に対してランダムに「ドロップアウト(つまりゼロ化)」を行うものです。例えば、ある層が訓練時にある入力サンプルに対して、普通は`[0.2, 0.5, 1.3, 0.8, 1.1]` というベクトルを出力するとします。ドロップアウトを適用すると、このベクトルは例えば`[0, 0.5, 1.3, 0, 1.1]`のようにランダムに散らばったいくつかのゼロを含むようになります。「ドロップアウト率」はゼロ化される特徴の割合で、通常は0.2から0.5の間に設定します。テスト時は、どのユニットもドロップアウトされず、代わりに出力値がドロップアウト率と同じ比率でスケールダウンされます。これは、訓練時に比べてたくさんのユニットがアクティブであることに対してバランスをとるためです。\n", - "\n", - "`tf.keras`では、Dropout層を使ってドロップアウトをネットワークに導入できます。ドロップアウト層は、その直前の層の出力に対してドロップアウトを適用します。\n", - "\n", - "それでは、IMDBネットワークに2つのドロップアウト層を追加しましょう。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "OFEYvtrHxSWS" - }, - "outputs": [], - "source": [ - "dpt_model = keras.models.Sequential([\n", - " keras.layers.Dense(16, activation='relu', input_shape=(NUM_WORDS,)),\n", - " keras.layers.Dropout(0.5),\n", - " keras.layers.Dense(16, activation='relu'),\n", - " keras.layers.Dropout(0.5),\n", - " keras.layers.Dense(1, activation='sigmoid')\n", - "])\n", - "\n", - "dpt_model.compile(optimizer='adam',\n", - " loss='binary_crossentropy',\n", - " metrics=['accuracy','binary_crossentropy'])\n", - "\n", - "dpt_model_history = dpt_model.fit(train_data, train_labels,\n", - " epochs=20,\n", - " batch_size=512,\n", - " validation_data=(test_data, test_labels),\n", - " verbose=2)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "SPZqwVchx5xp" - }, - "outputs": [], - "source": [ - "plot_history([('baseline', baseline_history),\n", - " ('dropout', dpt_model_history)])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "QioQ5e9GN0bM" - }, - "source": [ - "ドロップアウトを追加することで、比較対象モデルより明らかに改善が見られます。\n", - "\n", - "まとめ:ニューラルネットワークにおいて過学習を防ぐ最も一般的な方法は次のとおりです。\n", - "\n", - "* 訓練データを増やす\n", - "* ネットワークの容量をへらす\n", - "* 重みの正則化を行う\n", - "* ドロップアウトを追加する\n", - "\n", - "このガイドで触れていない2つの重要なアプローチがあります。データ拡張とバッチ正規化です。" - ] - } - ], - "metadata": { - "accelerator": "GPU", - "colab": { - "collapsed_sections": [ - "fTFj8ft5dlbS" - ], - "name": "overfit-and-underfit.ipynb のコピー", - "private_outputs": true, - "provenance": [], - "toc_visible": true, - "version": "0.3.2" - }, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - }, - "varInspector": { - "cols": { - "lenName": 16, - "lenType": 16, - "lenVar": 40 - }, - "kernels_config": { - "python": { - "delete_cmd_postfix": "", - "delete_cmd_prefix": "del ", - "library": "var_list.py", - "varRefreshCmd": "print(var_dic_list())" - }, - "r": { - "delete_cmd_postfix": ") ", - "delete_cmd_prefix": "rm(", - "library": "var_list.r", - "varRefreshCmd": "cat(var_dic_list()) " + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "overfit_and_underfit.ipynb", + "version": "0.3.2", + "provenance": [], + "private_outputs": true, + "collapsed_sections": [ + "fTFj8ft5dlbS" + ], + "toc_visible": true + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.5" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "fTFj8ft5dlbS" + }, + "source": [ + "##### Copyright 2018 The TensorFlow Authors." + ] + }, + { + "cell_type": "code", + "metadata": { + "cellView": "form", + "colab_type": "code", + "id": "lzyBOpYMdp3F", + "colab": {} + }, + "source": [ + "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "cellView": "form", + "colab_type": "code", + "id": "m_x4KfSJ7Vt7", + "colab": {} + }, + "source": [ + "#@title MIT License\n", + "#\n", + "# Copyright (c) 2017 François Chollet\n", + "#\n", + "# Permission is hereby granted, free of charge, to any person obtaining a\n", + "# copy of this software and associated documentation files (the \"Software\"),\n", + "# to deal in the Software without restriction, including without limitation\n", + "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n", + "# and/or sell copies of the Software, and to permit persons to whom the\n", + "# Software is furnished to do so, subject to the following conditions:\n", + "#\n", + "# The above copyright notice and this permission notice shall be included in\n", + "# all copies or substantial portions of the Software.\n", + "#\n", + "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n", + "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n", + "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n", + "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n", + "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n", + "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n", + "# DEALINGS IN THE SOFTWARE." + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "C9HmC2T4ld5B" + }, + "source": [ + "# 過学習と学習不足について知る" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "kRTxFhXAlnl1" + }, + "source": [ + "\n", + " \n", + " \n", + " \n", + "
\n", + " View on TensorFlow.org\n", + " \n", + " Run in Google Colab\n", + " \n", + " View source on GitHub\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "a0tw3CshaVrH", + "colab_type": "text" + }, + "source": [ + "Note: これらのドキュメントは私たちTensorFlowコミュニティが翻訳したものです。コミュニティによる 翻訳は**ベストエフォート**であるため、この翻訳が正確であることや[英語の公式ドキュメント](https://www.tensorflow.org/?hl=en)の 最新の状態を反映したものであることを保証することはできません。 この翻訳の品質を向上させるためのご意見をお持ちの方は、GitHubリポジトリ[tensorflow/docs](https://github.com/tensorflow/docs)にプルリクエストをお送りください。 コミュニティによる翻訳やレビューに参加していただける方は、 [docs-ja@tensorflow.org メーリングリスト](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ja)にご連絡ください。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "19rPukKZsPG6" + }, + "source": [ + "いつものように、この例のプログラムは`tf.keras` APIを使用します。詳しくはTensorFlowの[Keras guide](https://www.tensorflow.org/guide/keras)を参照してください。\n", + "\n", + "これまでの例、つまり、映画レビューの分類と燃費の推定では、検証用データでのモデルの正解率が、数エポックでピークを迎え、その後低下するという現象が見られました。\n", + "\n", + "言い換えると、モデルが訓練用データを**過学習**したと考えられます。過学習への対処の仕方を学ぶことは重要です。**訓練用データセット**で高い正解率を達成することは難しくありませんが、我々は、(これまで見たこともない)**テスト用データ**に汎化したモデルを開発したいのです。\n", + "\n", + "過学習の反対語は**学習不足**(underfitting)です。学習不足は、モデルがテストデータに対してまだ改善の余地がある場合に発生します。学習不足の原因は様々です。モデルが十分強力でないとか、正則化のしすぎだとか、単に訓練時間が短すぎるといった理由があります。学習不足は、訓練用データの中の関連したパターンを学習しきっていないということを意味します。\n", + "\n", + "モデルの訓練をやりすぎると、モデルは過学習を始め、訓練用データの中のパターンで、テストデータには一般的ではないパターンを学習します。我々は、過学習と学習不足の中間を目指す必要があります。これから見ていくように、ちょうどよいエポック数だけ訓練を行うというのは必要なスキルなのです。\n", + "\n", + "過学習を防止するための、最良の解決策は、より多くの訓練用データを使うことです。多くのデータで訓練を行えば行うほど、モデルは自然により汎化していく様になります。これが不可能な場合、次善の策は正則化のようなテクニックを使うことです。正則化は、モデルに保存される情報の量とタイプに制約を課すものです。ネットワークが少数のパターンしか記憶できなければ、最適化プロセスにより、最も主要なパターンのみを学習することになり、より汎化される可能性が高くなります。\n", + "\n", + "このノートブックでは、重みの正則化とドロップアウトという、よく使われる2つの正則化テクニックをご紹介します。これらを使って、IMDBの映画レビューを分類するノートブックの改善を図ります。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "5pZ8A2liqvgk", + "colab": {} + }, + "source": [ + "from __future__ import absolute_import, division, print_function\n", + "\n", + "!pip install tensorflow==2.0.0-alpha0\n", + "import tensorflow as tf\n", + "from tensorflow import keras\n", + "\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "\n", + "print(tf.__version__)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "1cweoTiruj8O" + }, + "source": [ + "## IMDBデータセットのダウンロード\n", + "\n", + "以前のノートブックで使用したエンベディングの代わりに、ここでは文をマルチホットエンコードします。このモデルは、訓練用データセットをすぐに過学習します。このモデルを使って、過学習がいつ起きるかということと、どうやって過学習と戦うかをデモします。\n", + "\n", + "リストをマルチホットエンコードすると言うのは、0と1のベクトルにするということです。具体的にいうと、例えば`[3, 5]`というシーケンスを、インデックス3と5の値が1で、それ以外がすべて0の、10,000次元のベクトルに変換するということを意味します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "QpzE4iqZtJly", + "colab": {} + }, + "source": [ + "NUM_WORDS = 10000\n", + "\n", + "(train_data, train_labels), (test_data, test_labels) = keras.datasets.imdb.load_data(num_words=NUM_WORDS)\n", + "\n", + "def multi_hot_sequences(sequences, dimension):\n", + " # 形状が (len(sequences), dimension)ですべて0の行列を作る\n", + " results = np.zeros((len(sequences), dimension))\n", + " for i, word_indices in enumerate(sequences):\n", + " results[i, word_indices] = 1.0 # 特定のインデックスに対してresults[i] を1に設定する\n", + " return results\n", + "\n", + "\n", + "train_data = multi_hot_sequences(train_data, dimension=NUM_WORDS)\n", + "test_data = multi_hot_sequences(test_data, dimension=NUM_WORDS)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "MzWVeXe3NBTn" + }, + "source": [ + "結果として得られるマルチホットベクトルの1つを見てみましょう。単語のインデックスは頻度順にソートされています。このため、インデックスが0に近いほど1が多く出現するはずです。分布を見てみましょう。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "71kr5rG4LkGM", + "colab": {} + }, + "source": [ + "plt.plot(train_data[0])" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "lglk41MwvU5o" + }, + "source": [ + "## 過学習のデモ\n", + "\n", + "過学習を防止するための最も単純な方法は、モデルのサイズ、すなわち、モデル内の学習可能なパラメータの数を小さくすることです(学習パラメータの数は、層の数と層ごとのユニット数で決まります)。ディープラーニングでは、モデルの学習可能なパラメータ数を、しばしばモデルの「キャパシティ」と呼びます。直感的に考えれば、パラメータ数の多いモデルほど「記憶容量」が大きくなり、訓練用のサンプルとその目的変数の間の辞書のようなマッピングをたやすく学習することができます。このマッピングには汎化能力がまったくなく、これまで見たことが無いデータを使って予測をする際には役に立ちません。\n", + "\n", + "ディープラーニングのモデルは訓練用データに適応しやすいけれど、本当のチャレレンジは汎化であって適応ではないということを、肝に銘じておく必要があります。\n", + "\n", + "一方、ネットワークの記憶容量が限られている場合、前述のようなマッピングを簡単に学習することはできません。損失を減らすためには、より予測能力が高い圧縮された表現を学習しなければなりません。同時に、モデルを小さくしすぎると、訓練用データに適応するのが難しくなります。「多すぎる容量」と「容量不足」の間にちょうどよい容量があるのです。\n", + "\n", + "残念ながら、(層の数や、層ごとの大きさといった)モデルの適切なサイズやアーキテクチャを決める魔法の方程式はありません。一連の異なるアーキテクチャを使って実験を行う必要があります。\n", + "\n", + "適切なモデルのサイズを見つけるには、比較的少ない層の数とパラメータから始めるのがベストです。それから、検証用データでの損失値の改善が見られなくなるまで、徐々に層の大きさを増やしたり、新たな層を加えたりします。映画レビューの分類ネットワークでこれを試してみましょう。\n", + "\n", + "比較基準として、```Dense```層だけを使ったシンプルなモデルを構築し、その後、それより小さいバージョンと大きいバージョンを作って比較します。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "_ReKHdC2EgVu" + }, + "source": [ + "### 比較基準を作る" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "QKgdXPx9usBa", + "colab": {} + }, + "source": [ + "baseline_model = keras.Sequential([\n", + " # `.summary` を見るために`input_shape`が必要 \n", + " keras.layers.Dense(16, activation='relu', input_shape=(NUM_WORDS,)),\n", + " keras.layers.Dense(16, activation='relu'),\n", + " keras.layers.Dense(1, activation='sigmoid')\n", + "])\n", + "\n", + "baseline_model.compile(optimizer='adam',\n", + " loss='binary_crossentropy',\n", + " metrics=['accuracy', 'binary_crossentropy'])\n", + "\n", + "baseline_model.summary()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "LqG3MXF5xSjR", + "colab": {} + }, + "source": [ + "baseline_history = baseline_model.fit(train_data,\n", + " train_labels,\n", + " epochs=20,\n", + " batch_size=512,\n", + " validation_data=(test_data, test_labels),\n", + " verbose=2)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "L-DGRBbGxI6G" + }, + "source": [ + "### より小さいモデルの構築" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "SrfoVQheYSO5" + }, + "source": [ + "今作成したばかりの比較基準となるモデルに比べて隠れユニット数が少ないモデルを作りましょう。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "jksi-XtaxDAh", + "colab": {} + }, + "source": [ + "smaller_model = keras.Sequential([\n", + " keras.layers.Dense(4, activation='relu', input_shape=(NUM_WORDS,)),\n", + " keras.layers.Dense(4, activation='relu'),\n", + " keras.layers.Dense(1, activation='sigmoid')\n", + "])\n", + "\n", + "smaller_model.compile(optimizer='adam',\n", + " loss='binary_crossentropy',\n", + " metrics=['accuracy', 'binary_crossentropy'])\n", + "\n", + "smaller_model.summary()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "jbngCZliYdma" + }, + "source": [ + "同じデータを使って訓練します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "Ofn1AwDhx-Fe", + "colab": {} + }, + "source": [ + "smaller_history = smaller_model.fit(train_data,\n", + " train_labels,\n", + " epochs=20,\n", + " batch_size=512,\n", + " validation_data=(test_data, test_labels),\n", + " verbose=2)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "vIPuf23FFaVn" + }, + "source": [ + "### より大きなモデルの構築\n", + "\n", + "練習として、より大きなモデルを作成し、どれほど急速に過学習が起きるかを見ることもできます。次はこのベンチマークに、この問題が必要とするよりはるかに容量の大きなネットワークを追加しましょう。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "ghQwwqwqvQM9", + "colab": {} + }, + "source": [ + "bigger_model = keras.models.Sequential([\n", + " keras.layers.Dense(512, activation='relu', input_shape=(NUM_WORDS,)),\n", + " keras.layers.Dense(512, activation='relu'),\n", + " keras.layers.Dense(1, activation='sigmoid')\n", + "])\n", + "\n", + "bigger_model.compile(optimizer='adam',\n", + " loss='binary_crossentropy',\n", + " metrics=['accuracy','binary_crossentropy'])\n", + "\n", + "bigger_model.summary()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "D-d-i5DaYmr7" + }, + "source": [ + "このモデルもまた同じデータを使って訓練します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "U1A99dhqvepf", + "colab": {} + }, + "source": [ + "bigger_history = bigger_model.fit(train_data, train_labels,\n", + " epochs=20,\n", + " batch_size=512,\n", + " validation_data=(test_data, test_labels),\n", + " verbose=2)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Fy3CMUZpzH3d" + }, + "source": [ + "### 訓練時と検証時の損失をグラフにする\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "HSlo1F4xHuuM" + }, + "source": [ + "実線は訓練用データセットの損失、破線は検証用データセットでの損失です(検証用データでの損失が小さい方が良いモデルです)。これをみると、小さいネットワークのほうが比較基準のモデルよりも過学習が始まるのが遅いことがわかります(4エポックではなく6エポック後)。また、過学習が始まっても性能の低下がよりゆっくりしています。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "0XmKDtOWzOpk", + "colab": {} + }, + "source": [ + "def plot_history(histories, key='binary_crossentropy'):\n", + " plt.figure(figsize=(16,10))\n", + " \n", + " for name, history in histories:\n", + " val = plt.plot(history.epoch, history.history['val_'+key],\n", + " '--', label=name.title()+' Val')\n", + " plt.plot(history.epoch, history.history[key], color=val[0].get_color(),\n", + " label=name.title()+' Train')\n", + "\n", + " plt.xlabel('Epochs')\n", + " plt.ylabel(key.replace('_',' ').title())\n", + " plt.legend()\n", + "\n", + " plt.xlim([0,max(history.epoch)])\n", + "\n", + "\n", + "plot_history([('baseline', baseline_history),\n", + " ('smaller', smaller_history),\n", + " ('bigger', bigger_history)])" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Bi6hBhdnSfjA" + }, + "source": [ + "より大きなネットワークでは、すぐに、1エポックで過学習が始まり、その度合も強いことに注目してください。ネットワークの容量が大きいほど訓練用データをモデル化するスピードが早くなり(結果として訓練時の損失値が小さくなり)ますが、より過学習しやすく(結果として訓練時の損失値と検証時の損失値が大きく乖離しやすく)なります。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "ASdv7nsgEFhx" + }, + "source": [ + "## 過学習防止の戦略" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "4rHoVWcswFLa" + }, + "source": [ + "### 重みの正則化を加える\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "kRxWepNawbBK" + }, + "source": [ + "「オッカムの剃刀」の原則をご存知でしょうか。何かの説明が2つあるとすると、最も正しいと考えられる説明は、仮定の数が最も少ない「一番単純な」説明だというものです。この原則は、ニューラルネットワークを使って学習されたモデルにも当てはまります。ある訓練用データとネットワーク構造があって、そのデータを説明できる重みの集合が複数ある時(つまり、複数のモデルがある時)、単純なモデルのほうが複雑なものよりも過学習しにくいのです。\n", + "\n", + "ここで言う「単純なモデル」とは、パラメータ値の分布のエントロピーが小さいもの(あるいは、上記で見たように、そもそもパラメータの数が少ないもの)です。したがって、過学習を緩和するための一般的な手法は、重みが小さい値のみをとることで、重み値の分布がより整然となる(正則)様に制約を与えるものです。これを「重みの正則化」と呼ばれ、ネットワークの損失関数に、重みの大きさに関連するコストを加えることで行われます。このコストには2つの種類があります。\n", + "\n", + "* [L1正則化](https://developers.google.com/machine-learning/glossary/#L1_regularization) 重み係数の絶対値に比例するコストを加える(重みの「L1ノルム」と呼ばれる)。\n", + "\n", + "* [L2正則化](https://developers.google.com/machine-learning/glossary/#L2_regularization) 重み係数の二乗に比例するコストを加える(重み係数の二乗「L2ノルム」と呼ばれる)。L2正則化はニューラルネットワーク用語では重み減衰(Weight Decay)と呼ばれる。呼び方が違うので混乱しないように。重み減衰は数学的にはL2正則化と同義である。\n", + "\n", + "L1正則化は重みパラメータの一部を0にすることでモデルを疎にする効果があります。L2正則化は重みパラメータにペナルティを加えますがモデルを疎にすることはありません。これは、L2正則化のほうが一般的である理由の一つです。\n", + "\n", + "`tf.keras`では、重みの正則化をするために、重み正則化のインスタンスをキーワード引数として層に加えます。ここでは、L2正則化を追加してみましょう。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "HFGmcwduwVyQ", + "colab": {} + }, + "source": [ + "l2_model = keras.models.Sequential([\n", + " keras.layers.Dense(16, kernel_regularizer=keras.regularizers.l2(0.001),\n", + " activation='relu', input_shape=(NUM_WORDS,)),\n", + " keras.layers.Dense(16, kernel_regularizer=keras.regularizers.l2(0.001),\n", + " activation='relu'),\n", + " keras.layers.Dense(1, activation='sigmoid')\n", + "])\n", + "\n", + "l2_model.compile(optimizer='adam',\n", + " loss='binary_crossentropy',\n", + " metrics=['accuracy', 'binary_crossentropy'])\n", + "\n", + "l2_model_history = l2_model.fit(train_data, train_labels,\n", + " epochs=20,\n", + " batch_size=512,\n", + " validation_data=(test_data, test_labels),\n", + " verbose=2)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "bUUHoXb7w-_C" + }, + "source": [ + "```l2(0.001)```というのは、層の重み行列の係数全てに対して```0.001 * 重み係数の値 **2```をネットワークの損失値合計に加えることを意味します。このペナルティは訓練時のみに加えられるため、このネットワークの損失値は、訓練時にはテスト時に比べて大きくなることに注意してください。\n", + "\n", + "L2正則化の影響を見てみましょう。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "7wkfLyxBZdh_", + "colab": {} + }, + "source": [ + "plot_history([('baseline', baseline_history),\n", + " ('l2', l2_model_history)])" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Kx1YHMsVxWjP" + }, + "source": [ + "ご覧のように、L2正則化ありのモデルは比較基準のモデルに比べて過学習しにくくなっています。両方のモデルのパラメータ数は同じであるにもかかわらずです。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "HmnBNOOVxiG8" + }, + "source": [ + "### ドロップアウトを追加する\n", + "\n", + "ドロップアウトは、ニューラルネットワークの正則化テクニックとして最もよく使われる手法の一つです。この手法は、トロント大学のヒントンと彼の学生が開発したものです。ドロップアウトは層に適用するもので、訓練時に層から出力された特徴量に対してランダムに「ドロップアウト(つまりゼロ化)」を行うものです。例えば、ある層が訓練時にある入力サンプルに対して、普通は`[0.2, 0.5, 1.3, 0.8, 1.1]` というベクトルを出力するとします。ドロップアウトを適用すると、このベクトルは例えば`[0, 0.5, 1.3, 0, 1.1]`のようにランダムに散らばったいくつかのゼロを含むようになります。「ドロップアウト率」はゼロ化される特徴の割合で、通常は0.2から0.5の間に設定します。テスト時は、どのユニットもドロップアウトされず、代わりに出力値がドロップアウト率と同じ比率でスケールダウンされます。これは、訓練時に比べてたくさんのユニットがアクティブであることに対してバランスをとるためです。\n", + "\n", + "`tf.keras`では、Dropout層を使ってドロップアウトをネットワークに導入できます。ドロップアウト層は、その直前の層の出力に対してドロップアウトを適用します。\n", + "\n", + "それでは、IMDBネットワークに2つのドロップアウト層を追加しましょう。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "OFEYvtrHxSWS", + "colab": {} + }, + "source": [ + "dpt_model = keras.models.Sequential([\n", + " keras.layers.Dense(16, activation='relu', input_shape=(NUM_WORDS,)),\n", + " keras.layers.Dropout(0.5),\n", + " keras.layers.Dense(16, activation='relu'),\n", + " keras.layers.Dropout(0.5),\n", + " keras.layers.Dense(1, activation='sigmoid')\n", + "])\n", + "\n", + "dpt_model.compile(optimizer='adam',\n", + " loss='binary_crossentropy',\n", + " metrics=['accuracy','binary_crossentropy'])\n", + "\n", + "dpt_model_history = dpt_model.fit(train_data, train_labels,\n", + " epochs=20,\n", + " batch_size=512,\n", + " validation_data=(test_data, test_labels),\n", + " verbose=2)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "SPZqwVchx5xp", + "colab": {} + }, + "source": [ + "plot_history([('baseline', baseline_history),\n", + " ('dropout', dpt_model_history)])" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "QioQ5e9GN0bM" + }, + "source": [ + "ドロップアウトを追加することで、比較対象モデルより明らかに改善が見られます。\n", + "\n", + "まとめ:ニューラルネットワークにおいて過学習を防ぐ最も一般的な方法は次のとおりです。\n", + "\n", + "* 訓練データを増やす\n", + "* ネットワークの容量をへらす\n", + "* 重みの正則化を行う\n", + "* ドロップアウトを追加する\n", + "\n", + "このガイドで触れていない2つの重要なアプローチがあります。データ拡張とバッチ正規化です。" + ] } - }, - "types_to_exclude": [ - "module", - "function", - "builtin_function_or_method", - "instance", - "_Feature" - ], - "window_display": false - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + ] +} \ No newline at end of file diff --git a/site/ja/r2/tutorials/keras/save_and_restore_models.ipynb b/site/ja/r2/tutorials/keras/save_and_restore_models.ipynb index 4e14c7e69d3..bce22a24683 100644 --- a/site/ja/r2/tutorials/keras/save_and_restore_models.ipynb +++ b/site/ja/r2/tutorials/keras/save_and_restore_models.ipynb @@ -1,876 +1,890 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "g_nWetWWd_ns" - }, - "source": [ - "##### Copyright 2018 The TensorFlow Authors." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "cellView": "form", - "colab": {}, - "colab_type": "code", - "id": "2pHVBk_seED1" - }, - "outputs": [], - "source": [ - "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", - "# you may not use this file except in compliance with the License.\n", - "# You may obtain a copy of the License at\n", - "#\n", - "# https://www.apache.org/licenses/LICENSE-2.0\n", - "#\n", - "# Unless required by applicable law or agreed to in writing, software\n", - "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", - "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", - "# See the License for the specific language governing permissions and\n", - "# limitations under the License." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "cellView": "form", - "colab": {}, - "colab_type": "code", - "id": "N_fMsQ-N8I7j" - }, - "outputs": [], - "source": [ - "#@title MIT License\n", - "#\n", - "# Copyright (c) 2017 François Chollet\n", - "#\n", - "# Permission is hereby granted, free of charge, to any person obtaining a\n", - "# copy of this software and associated documentation files (the \"Software\"),\n", - "# to deal in the Software without restriction, including without limitation\n", - "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n", - "# and/or sell copies of the Software, and to permit persons to whom the\n", - "# Software is furnished to do so, subject to the following conditions:\n", - "#\n", - "# The above copyright notice and this permission notice shall be included in\n", - "# all copies or substantial portions of the Software.\n", - "#\n", - "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n", - "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n", - "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n", - "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n", - "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n", - "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n", - "# DEALINGS IN THE SOFTWARE." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "pZJ3uY9O17VN" - }, - "source": [ - "# モデルの保存と復元" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "M4Ata7_wMul1" - }, - "source": [ - "\n", - " \n", - " \n", - " \n", - "
\n", - " View on TensorFlow.org\n", - " \n", - " Run in Google Colab\n", - " \n", - " View source on GitHub\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "mBdde4YJeJKF" - }, - "source": [ - "モデルは訓練中にも、訓練が終わったあとも保存できます。このことは、長い訓練時間を掛けなくても、やめたところから再開できるということを意味します。モデルが保存可能であることは、あなたが作ったモデルを他の人と共有できるということでもあります。研究結果であるモデルや手法を公開する際、機械学習の実務家はほとんど次のものを共有します。\n", - "\n", - "* モデルを構築するプログラム\n", - "* 学習済みモデルの重みあるいはパラメータ\n", - "\n", - "このデータを共有することで、他の人がモデルだどの様に動作するかを理解したり、新しいデータに試してみたりすることが容易になります。\n", - "\n", - "注意:信頼できないプログラムには気をつけましょう。TensorFlowのモデルもプログラムです。詳しくは、[Using TensorFlow Securely](https://github.com/tensorflow/tensorflow/blob/master/SECURITY.md)を参照してください。\n", - "\n", - "### オプション\n", - "\n", - "TensorFlowのモデルを保存する方法は、使っているAPIによって異なります。このガイドはTensorFlowのモデルを構築し訓練するためのハイレベルなAPIである[tf.keras](https://www.tensorflow.org/guide/keras)を使っています。この他のアプローチについては、TensorFlowの [Save and Restore](https://www.tensorflow.org/guide/saved_model) ガイド、あるいは、[Saving in eager](https://www.tensorflow.org/guide/eager#object-based_saving)を参照してください。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "xCUREq7WXgvg" - }, - "source": [ - "## 設定\n", - "\n", - "### インストールとインポート" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "7l0MiTOrXtNv" - }, - "source": [ - "TensorFlowと依存関係のライブラリをインストールし、インポートします。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "RzIOVSdnMYyO" - }, - "outputs": [], - "source": [ - "!pip install h5py pyyaml " - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "SbGsznErXWt6" - }, - "source": [ - "### サンプルデータセットの取得\n", - "\n", - "ここでは、モデルを訓練し重みの保存をデモするために、 [MNIST dataset](http://yann.lecun.com/exdb/mnist/) を使います。デモの実行を速くするため、最初の1,000件のサンプルだけを使います。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "7Nm7Tyb-gRt-" - }, - "outputs": [], - "source": [ - "from __future__ import absolute_import, division, print_function\n", - "\n", - "import os\n", - "\n", - "!pip install tensorflow==2.0.0-alpha0\n", - "import tensorflow as tf\n", - "from tensorflow import keras\n", - "\n", - "tf.__version__" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "9rGfFwE9XVwz" - }, - "outputs": [], - "source": [ - "(train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.mnist.load_data()\n", - "\n", - "train_labels = train_labels[:1000]\n", - "test_labels = test_labels[:1000]\n", - "\n", - "train_images = train_images[:1000].reshape(-1, 28 * 28) / 255.0\n", - "test_images = test_images[:1000].reshape(-1, 28 * 28) / 255.0" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "anG3iVoXyZGI" - }, - "source": [ - "### モデルの定義" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "wynsOBfby0Pa" - }, - "source": [ - "重みの保存と読み込みのデモを行うための簡単なモデルを定義しましょう。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "0HZbJIjxyX1S" - }, - "outputs": [], - "source": [ - "# 短いシーケンシャルモデルを返す関数\n", - "def create_model():\n", - " model = tf.keras.models.Sequential([\n", - " keras.layers.Dense(512, activation='relu', input_shape=(784,)),\n", - " keras.layers.Dropout(0.2),\n", - " keras.layers.Dense(10, activation='softmax')\n", - " ])\n", - " \n", - " model.compile(optimizer='adam', \n", - " loss='sparse_categorical_crossentropy',\n", - " metrics=['accuracy'])\n", - " \n", - " return model\n", - "\n", - "\n", - "# 基本的なモデルのインスタンスを作る\n", - "model = create_model()\n", - "model.summary()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "soDE0W_KH8rG" - }, - "source": [ - "## 訓練中にチェックポイントを保存する" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "mRyd5qQQIXZm" - }, - "source": [ - "主な用途は訓練の**途中**あるいは**終了後**にチェックポイントを自動的に保存することです。こうすることにより、再び訓練を行うことなくモデルを使用することができ、また、訓練が中断された場合に、中止したところから再開できます。\n", - "\n", - "`tf.keras.callbacks.ModelCheckpoint`がこれを行うためのコールバックです。このコールバックにはチェックポイントを構成するためのいくつかの引数があります。\n", - "\n", - "### チェックポイントコールバックの使い方\n", - "\n", - "モデルの訓練時に、`ModelCheckpoint`を渡します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "IFPuhwntH8VH" - }, - "outputs": [], - "source": [ - "checkpoint_path = \"training_1/cp.ckpt\"\n", - "checkpoint_dir = os.path.dirname(checkpoint_path)\n", - "\n", - "# チェックポイントコールバックを作る\n", - "cp_callback = tf.keras.callbacks.ModelCheckpoint(checkpoint_path, \n", - " save_weights_only=True,\n", - " verbose=1)\n", - "\n", - "model = create_model()\n", - "\n", - "model.fit(train_images, train_labels, epochs = 10, \n", - " validation_data = (test_images,test_labels),\n", - " callbacks = [cp_callback]) # 訓練にコールバックを渡す\n", - "\n", - "# オプティマイザの状態保存についての警告が表示されるかもしれません。\n", - "# これらの警告は(このノートブックで発生する同様な警告を含めて)\n", - "# 古い用法を非推奨にするためのもので、無視して構いません。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "rlM-sgyJO084" - }, - "source": [ - "この結果、エポックごとに更新される一連のTensorFlowチェックポイントファイルが作成されます。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "gXG5FVKFOVQ3" - }, - "outputs": [], - "source": [ - "!ls {checkpoint_dir}" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "wlRN_f56Pqa9" - }, - "source": [ - "訓練していない新しいモデルを作ります。重みだけからモデルを復元する場合には、元のモデルと同じアーキテクチャのモデルが必要です。モデルのアーキテクチャが同じであるため、モデルの異なる**インスタンス**であっても重みを共有することができるのです。\n", - "\n", - "訓練していない全く新しいモデルを作り、テストデータセットで評価します。訓練をしていないモデルは偶然のレベル(正解率10%以下)の性能しか無いはずです。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "Fp5gbuiaPqCT" - }, - "outputs": [], - "source": [ - "model = create_model()\n", - "\n", - "loss, acc = model.evaluate(test_images, test_labels)\n", - "print(\"Untrained model, accuracy: {:5.2f}%\".format(100*acc))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "1DTKpZssRSo3" - }, - "source": [ - "次に、チェックポイントから重みをロードし、再び評価します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "2IZxbwiRRSD2" - }, - "outputs": [], - "source": [ - "model.load_weights(checkpoint_path)\n", - "loss,acc = model.evaluate(test_images, test_labels)\n", - "print(\"Restored model, accuracy: {:5.2f}%\".format(100*acc))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "bpAbKkAyVPV8" - }, - "source": [ - "### チェックポイントコールバックのオプション\n", - "\n", - "このコールバックには、チェックポイントに一意な名前をつけたり、チェックポイントの頻度を調整するためのオプションがあります。\n", - "\n", - "新しいモデルを訓練し、5エポックごとに一意な名前のチェックポイントを保存します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "mQF_dlgIVOvq" - }, - "outputs": [], - "source": [ - "# ファイル名に(`str.format`を使って)エポック数を埋め込みます\n", - "checkpoint_path = \"training_2/cp-{epoch:04d}.ckpt\"\n", - "checkpoint_dir = os.path.dirname(checkpoint_path)\n", - "\n", - "cp_callback = tf.keras.callbacks.ModelCheckpoint(\n", - " checkpoint_path, verbose=1, save_weights_only=True,\n", - " # 重みを5エポックごとに保存します\n", - " period=5)\n", - "\n", - "model = create_model()\n", - "model.fit(train_images, train_labels,\n", - " epochs = 50, callbacks = [cp_callback],\n", - " validation_data = (test_images,test_labels),\n", - " verbose=0)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "1zFrKTjjavWI" - }, - "source": [ - "次に、出来上がったチェックポイントを確認し、最後のものを選択します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "p64q3-V4sXt0" - }, - "outputs": [], - "source": [ - "! ls {checkpoint_dir}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "1AN_fnuyR41H" - }, - "outputs": [], - "source": [ - "latest = tf.train.latest_checkpoint(checkpoint_dir)\n", - "latest" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "Zk2ciGbKg561" - }, - "source": [ - "注意:デフォルトのtensorflowフォーマットは、直近の5つのチェックポイントのみを保存します。\n", - "\n", - "テストのため、モデルをリセットし最後のチェックポイントをロードします。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "3M04jyK-H3QK" - }, - "outputs": [], - "source": [ - "model = create_model()\n", - "model.load_weights(latest)\n", - "loss, acc = model.evaluate(test_images, test_labels)\n", - "print(\"Restored model, accuracy: {:5.2f}%\".format(100*acc))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "c2OxsJOTHxia" - }, - "source": [ - "## これらのファイルは何?" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "JtdYhvWnH2ib" - }, - "source": [ - "上記のコードでは、重みだけをバイナリで[checkpoint](https://www.tensorflow.org/guide/saved_model#save_and_restore_variables)形式の一連のファイルに保存します。チェックポイントには、次のものが含まれます。\n", - "\n", - "* 1つ以上のモデルの重みの断片\n", - "* どの重みがどの断片に保存されているかを示すインデックスファイル\n", - "\n", - "1台のマシンだけでモデルの訓練を行っている場合には、`.data-00000-of-00001`のようなサフィックスのついたファイルが1つだけ作成されます。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "S_FA-ZvxuXQV" - }, - "source": [ - "## 手動で重みを保存する\n", - "\n", - "上記では重みをモデルにロードする方法を見ました。\n", - "\n", - "手動で重みを保存するのも同じ様に簡単です。`Model.save_weights` メソッドを使います。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "R7W5plyZ-u9X" - }, - "outputs": [], - "source": [ - "# 重みの保存\n", - "model.save_weights('./checkpoints/my_checkpoint')\n", - "\n", - "# 重みの復元\n", - "model = create_model()\n", - "model.load_weights('./checkpoints/my_checkpoint')\n", - "\n", - "loss,acc = model.evaluate(test_images, test_labels)\n", - "print(\"Restored model, accuracy: {:5.2f}%\".format(100*acc))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "kOGlxPRBEvV1" - }, - "source": [ - "## モデル全体の保存\n", - "\n", - "モデルとオプティマイザを、その状態(重みと変数)とモデルの設定の両方を含む1つのファイルに保存することができます。これにより、モデルをオリジナルのPythonコードにアクセスしなくとも使用できるようにエクスポートできます。オプティマイザの状態が復元されるので、中断したところから訓練を再開することも可能です。\n", - "\n", - "完全に機能するモデルを保存できるのは便利です。保存したモデルをTensorFlow.js ([HDF5](https://js.tensorflow.org/tutorials/import-keras.html), [Saved Model](https://js.tensorflow.org/tutorials/import-saved-model.html))でロードし、ブラウザで訓練したり、実行したりすることができるほか、TensorFlow Lite ([HDF5](https://www.tensorflow.org/lite/convert/python_api#exporting_a_tfkeras_file_), [Saved Model](https://www.tensorflow.org/lite/convert/python_api#exporting_a_savedmodel_))\n", - "を使ってモバイルデバイスで実行できるように変換することも可能です。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "SkGwf-50zLNn" - }, - "source": [ - "### HDF5ファイルとして\n", - "\n", - "Kerasでは、[HDF5](https://en.wikipedia.org/wiki/Hierarchical_Data_Format) 標準を使った基本的なファイルフォーマットが利用できます。ここでの利用目的では、保存されたモデルは単独のバイナリラージオブジェクト(blob)として扱うことができます。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "m2dkmJVCGUia" - }, - "outputs": [], - "source": [ - "model = create_model()\n", - "\n", - "model.fit(train_images, train_labels, epochs=5)\n", - "\n", - "# モデル全体を1つのHDF5ファイルに保存します。\n", - "model.save('my_model.h5')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "GWmttMOqS68S" - }, - "source": [ - "保存したファイルを使ってモデルを再作成します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "5NDMO_7kS6Do" - }, - "outputs": [], - "source": [ - "# 重みとオプティマイザを含む全く同じモデルを再作成\n", - "new_model = keras.models.load_model('my_model.h5')\n", - "new_model.summary()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "JXQpbTicTBwt" - }, - "source": [ - "正解率を検査します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "jwEaj9DnTCVA" - }, - "outputs": [], - "source": [ - "loss, acc = new_model.evaluate(test_images, test_labels)\n", - "print(\"Restored model, accuracy: {:5.2f}%\".format(100*acc))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "dGXqd4wWJl8O" - }, - "source": [ - "この方法では、次のすべてが保存されます。\n", - "\n", - "* 重みの値\n", - "* モデルの設定(アーキテクチャ)\n", - "* オプティマイザの設定\n", - "\n", - "Kerasは保存する際にアーキテクチャを調べます。いまのところ、TensorFlowのオプティマイザ(`tf.train`に含まれるもの)を保存することはできません。TensorFlowのオプティマイザを使用している場合には、モデルをロードしたあと再コンパイルする必要があり、オプティマイザの状態は失われます。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "kPyhgcoVzqUB" - }, - "source": [ - "### `saved_model`として" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "LtcN4VIb7JkK" - }, - "source": [ - "注意:この手法による`tf.keras`モデルの保存は実験的なもので、将来のバージョンで変更される可能性があります。" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "DSWiSB0Q8c46" - }, - "source": [ - "新しいモデルを作ります。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "sI1YvCDFzpl3" - }, - "outputs": [], - "source": [ - "model = create_model()\n", - "\n", - "model.fit(train_images, train_labels, epochs=5)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "iUvT_3qE8hV5" - }, - "source": [ - "`saved_model`を作成し、タイムスタンプ付きのディレクトリに保存します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "sq8fPglI1RWA", - "scrolled": true - }, - "outputs": [], - "source": [ - "import time\n", - "saved_model_path = \"./saved_models/{}\".format(int(time.time()))\n", - "\n", - "tf.keras.experimental.export_saved_model(model, saved_model_path)\n", - "saved_model_path" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "MjpmyPfh8-1n" - }, - "source": [ - "作成したsaved_modelsを一覧表示します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "ZtOvxA7V0iTv" - }, - "outputs": [], - "source": [ - "!ls saved_models/" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "B7qfpvpY9HCe" - }, - "source": [ - "保存されたモデル(SavedModel)から新しいKerasモデルをリロードします。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "0YofwHdN0pxa" - }, - "outputs": [], - "source": [ - "new_model = tf.keras.experimental.load_from_saved_model(saved_model_path)\n", - "new_model.summary()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "uWwgNaz19TH2" - }, - "source": [ - "復元されたモデルを実行します。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "model.predict(test_images).shape" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "Pc9e6G6w1AWG", - "scrolled": true - }, - "outputs": [], - "source": [ - "# モデルを評価する前にコンパイルする必要があります。\n", - "# モデルをデプロイするだけであればこのステップは不要です。\n", - "\n", - "new_model.compile(optimizer=model.optimizer, # ロードしてあったオプティマイザを保持\n", - " loss='sparse_categorical_crossentropy',\n", - " metrics=['accuracy'])\n", - "\n", - "# モデルを評価します。\n", - "loss, acc = new_model.evaluate(test_images, test_labels)\n", - "print(\"Restored model, accuracy: {:5.2f}%\".format(100*acc))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "eUYTzSz5VxL2" - }, - "source": [ - "## この先は?\n", - "\n", - "`tf.keras`を使った保存とロードのクイックガイドでした。\n", - "\n", - "* [tf.keras guide](https://www.tensorflow.org/guide/keras) には`tf.keras`での保存とロードについて、もう少し記載されています\n", - "\n", - "* Eager Executionでの保存については[Saving in eager](https://www.tensorflow.org/guide/eager#object_based_saving) を参照ください\n", - "\n", - "* [Save and Restore](https://www.tensorflow.org/guide/saved_model)ガイドには、TensorFlowでの保存についてローレベルの詳細が記載されています" - ] - } - ], - "metadata": { - "accelerator": "GPU", - "colab": { - "collapsed_sections": [], - "name": "save_and_restore_models.ipynb", - "private_outputs": true, - "provenance": [], - "toc_visible": true, - "version": "0.3.2" - }, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "save_and_restore_models.ipynb", + "version": "0.3.2", + "provenance": [], + "private_outputs": true, + "collapsed_sections": [], + "toc_visible": true + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.5" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "g_nWetWWd_ns" + }, + "source": [ + "##### Copyright 2018 The TensorFlow Authors." + ] + }, + { + "cell_type": "code", + "metadata": { + "cellView": "form", + "colab_type": "code", + "id": "2pHVBk_seED1", + "colab": {} + }, + "source": [ + "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "cellView": "form", + "colab_type": "code", + "id": "N_fMsQ-N8I7j", + "colab": {} + }, + "source": [ + "#@title MIT License\n", + "#\n", + "# Copyright (c) 2017 François Chollet\n", + "#\n", + "# Permission is hereby granted, free of charge, to any person obtaining a\n", + "# copy of this software and associated documentation files (the \"Software\"),\n", + "# to deal in the Software without restriction, including without limitation\n", + "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n", + "# and/or sell copies of the Software, and to permit persons to whom the\n", + "# Software is furnished to do so, subject to the following conditions:\n", + "#\n", + "# The above copyright notice and this permission notice shall be included in\n", + "# all copies or substantial portions of the Software.\n", + "#\n", + "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n", + "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n", + "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n", + "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n", + "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n", + "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n", + "# DEALINGS IN THE SOFTWARE." + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "pZJ3uY9O17VN" + }, + "source": [ + "# モデルの保存と復元" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "M4Ata7_wMul1" + }, + "source": [ + "\n", + " \n", + " \n", + " \n", + "
\n", + " View on TensorFlow.org\n", + " \n", + " Run in Google Colab\n", + " \n", + " View source on GitHub\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0dYY-BkJaqKb", + "colab_type": "text" + }, + "source": [ + "Note: これらのドキュメントは私たちTensorFlowコミュニティが翻訳したものです。コミュニティによる 翻訳は**ベストエフォート**であるため、この翻訳が正確であることや[英語の公式ドキュメント](https://www.tensorflow.org/?hl=en)の 最新の状態を反映したものであることを保証することはできません。 この翻訳の品質を向上させるためのご意見をお持ちの方は、GitHubリポジトリ[tensorflow/docs](https://github.com/tensorflow/docs)にプルリクエストをお送りください。 コミュニティによる翻訳やレビューに参加していただける方は、 [docs-ja@tensorflow.org メーリングリスト](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ja)にご連絡ください。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "mBdde4YJeJKF" + }, + "source": [ + "モデルは訓練中にも、訓練が終わったあとも保存できます。このことは、長い訓練時間を掛けなくても、やめたところから再開できるということを意味します。モデルが保存可能であることは、あなたが作ったモデルを他の人と共有できるということでもあります。研究結果であるモデルや手法を公開する際、機械学習の実務家はほとんど次のものを共有します。\n", + "\n", + "* モデルを構築するプログラム\n", + "* 学習済みモデルの重みあるいはパラメータ\n", + "\n", + "このデータを共有することで、他の人がモデルだどの様に動作するかを理解したり、新しいデータに試してみたりすることが容易になります。\n", + "\n", + "注意:信頼できないプログラムには気をつけましょう。TensorFlowのモデルもプログラムです。詳しくは、[Using TensorFlow Securely](https://github.com/tensorflow/tensorflow/blob/master/SECURITY.md)を参照してください。\n", + "\n", + "### オプション\n", + "\n", + "TensorFlowのモデルを保存する方法は、使っているAPIによって異なります。このガイドはTensorFlowのモデルを構築し訓練するためのハイレベルなAPIである[tf.keras](https://www.tensorflow.org/guide/keras)を使っています。この他のアプローチについては、TensorFlowの [Save and Restore](https://www.tensorflow.org/guide/saved_model) ガイド、あるいは、[Saving in eager](https://www.tensorflow.org/guide/eager#object-based_saving)を参照してください。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "xCUREq7WXgvg" + }, + "source": [ + "## 設定\n", + "\n", + "### インストールとインポート" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "7l0MiTOrXtNv" + }, + "source": [ + "TensorFlowと依存関係のライブラリをインストールし、インポートします。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "RzIOVSdnMYyO", + "colab": {} + }, + "source": [ + "!pip install h5py pyyaml " + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "SbGsznErXWt6" + }, + "source": [ + "### サンプルデータセットの取得\n", + "\n", + "ここでは、モデルを訓練し重みの保存をデモするために、 [MNIST dataset](http://yann.lecun.com/exdb/mnist/) を使います。デモの実行を速くするため、最初の1,000件のサンプルだけを使います。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "7Nm7Tyb-gRt-", + "colab": {} + }, + "source": [ + "from __future__ import absolute_import, division, print_function\n", + "\n", + "import os\n", + "\n", + "!pip install tensorflow==2.0.0-alpha0\n", + "import tensorflow as tf\n", + "from tensorflow import keras\n", + "\n", + "tf.__version__" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "9rGfFwE9XVwz", + "colab": {} + }, + "source": [ + "(train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.mnist.load_data()\n", + "\n", + "train_labels = train_labels[:1000]\n", + "test_labels = test_labels[:1000]\n", + "\n", + "train_images = train_images[:1000].reshape(-1, 28 * 28) / 255.0\n", + "test_images = test_images[:1000].reshape(-1, 28 * 28) / 255.0" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "anG3iVoXyZGI" + }, + "source": [ + "### モデルの定義" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "wynsOBfby0Pa" + }, + "source": [ + "重みの保存と読み込みのデモを行うための簡単なモデルを定義しましょう。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "0HZbJIjxyX1S", + "colab": {} + }, + "source": [ + "# 短いシーケンシャルモデルを返す関数\n", + "def create_model():\n", + " model = tf.keras.models.Sequential([\n", + " keras.layers.Dense(512, activation='relu', input_shape=(784,)),\n", + " keras.layers.Dropout(0.2),\n", + " keras.layers.Dense(10, activation='softmax')\n", + " ])\n", + " \n", + " model.compile(optimizer='adam', \n", + " loss='sparse_categorical_crossentropy',\n", + " metrics=['accuracy'])\n", + " \n", + " return model\n", + "\n", + "\n", + "# 基本的なモデルのインスタンスを作る\n", + "model = create_model()\n", + "model.summary()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "soDE0W_KH8rG" + }, + "source": [ + "## 訓練中にチェックポイントを保存する" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "mRyd5qQQIXZm" + }, + "source": [ + "主な用途は訓練の**途中**あるいは**終了後**にチェックポイントを自動的に保存することです。こうすることにより、再び訓練を行うことなくモデルを使用することができ、また、訓練が中断された場合に、中止したところから再開できます。\n", + "\n", + "`tf.keras.callbacks.ModelCheckpoint`がこれを行うためのコールバックです。このコールバックにはチェックポイントを構成するためのいくつかの引数があります。\n", + "\n", + "### チェックポイントコールバックの使い方\n", + "\n", + "モデルの訓練時に、`ModelCheckpoint`を渡します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "IFPuhwntH8VH", + "colab": {} + }, + "source": [ + "checkpoint_path = \"training_1/cp.ckpt\"\n", + "checkpoint_dir = os.path.dirname(checkpoint_path)\n", + "\n", + "# チェックポイントコールバックを作る\n", + "cp_callback = tf.keras.callbacks.ModelCheckpoint(checkpoint_path, \n", + " save_weights_only=True,\n", + " verbose=1)\n", + "\n", + "model = create_model()\n", + "\n", + "model.fit(train_images, train_labels, epochs = 10, \n", + " validation_data = (test_images,test_labels),\n", + " callbacks = [cp_callback]) # 訓練にコールバックを渡す\n", + "\n", + "# オプティマイザの状態保存についての警告が表示されるかもしれません。\n", + "# これらの警告は(このノートブックで発生する同様な警告を含めて)\n", + "# 古い用法を非推奨にするためのもので、無視して構いません。" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "rlM-sgyJO084" + }, + "source": [ + "この結果、エポックごとに更新される一連のTensorFlowチェックポイントファイルが作成されます。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "gXG5FVKFOVQ3", + "colab": {} + }, + "source": [ + "!ls {checkpoint_dir}" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "wlRN_f56Pqa9" + }, + "source": [ + "訓練していない新しいモデルを作ります。重みだけからモデルを復元する場合には、元のモデルと同じアーキテクチャのモデルが必要です。モデルのアーキテクチャが同じであるため、モデルの異なる**インスタンス**であっても重みを共有することができるのです。\n", + "\n", + "訓練していない全く新しいモデルを作り、テストデータセットで評価します。訓練をしていないモデルは偶然のレベル(正解率10%以下)の性能しか無いはずです。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "Fp5gbuiaPqCT", + "colab": {} + }, + "source": [ + "model = create_model()\n", + "\n", + "loss, acc = model.evaluate(test_images, test_labels)\n", + "print(\"Untrained model, accuracy: {:5.2f}%\".format(100*acc))" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "1DTKpZssRSo3" + }, + "source": [ + "次に、チェックポイントから重みをロードし、再び評価します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "2IZxbwiRRSD2", + "colab": {} + }, + "source": [ + "model.load_weights(checkpoint_path)\n", + "loss,acc = model.evaluate(test_images, test_labels)\n", + "print(\"Restored model, accuracy: {:5.2f}%\".format(100*acc))" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "bpAbKkAyVPV8" + }, + "source": [ + "### チェックポイントコールバックのオプション\n", + "\n", + "このコールバックには、チェックポイントに一意な名前をつけたり、チェックポイントの頻度を調整するためのオプションがあります。\n", + "\n", + "新しいモデルを訓練し、5エポックごとに一意な名前のチェックポイントを保存します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "mQF_dlgIVOvq", + "colab": {} + }, + "source": [ + "# ファイル名に(`str.format`を使って)エポック数を埋め込みます\n", + "checkpoint_path = \"training_2/cp-{epoch:04d}.ckpt\"\n", + "checkpoint_dir = os.path.dirname(checkpoint_path)\n", + "\n", + "cp_callback = tf.keras.callbacks.ModelCheckpoint(\n", + " checkpoint_path, verbose=1, save_weights_only=True,\n", + " # 重みを5エポックごとに保存します\n", + " period=5)\n", + "\n", + "model = create_model()\n", + "model.fit(train_images, train_labels,\n", + " epochs = 50, callbacks = [cp_callback],\n", + " validation_data = (test_images,test_labels),\n", + " verbose=0)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "1zFrKTjjavWI" + }, + "source": [ + "次に、出来上がったチェックポイントを確認し、最後のものを選択します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "p64q3-V4sXt0", + "colab": {} + }, + "source": [ + "! ls {checkpoint_dir}" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "1AN_fnuyR41H", + "colab": {} + }, + "source": [ + "latest = tf.train.latest_checkpoint(checkpoint_dir)\n", + "latest" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Zk2ciGbKg561" + }, + "source": [ + "注意:デフォルトのtensorflowフォーマットは、直近の5つのチェックポイントのみを保存します。\n", + "\n", + "テストのため、モデルをリセットし最後のチェックポイントをロードします。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "3M04jyK-H3QK", + "colab": {} + }, + "source": [ + "model = create_model()\n", + "model.load_weights(latest)\n", + "loss, acc = model.evaluate(test_images, test_labels)\n", + "print(\"Restored model, accuracy: {:5.2f}%\".format(100*acc))" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "c2OxsJOTHxia" + }, + "source": [ + "## これらのファイルは何?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "JtdYhvWnH2ib" + }, + "source": [ + "上記のコードでは、重みだけをバイナリで[checkpoint](https://www.tensorflow.org/guide/saved_model#save_and_restore_variables)形式の一連のファイルに保存します。チェックポイントには、次のものが含まれます。\n", + "\n", + "* 1つ以上のモデルの重みの断片\n", + "* どの重みがどの断片に保存されているかを示すインデックスファイル\n", + "\n", + "1台のマシンだけでモデルの訓練を行っている場合には、`.data-00000-of-00001`のようなサフィックスのついたファイルが1つだけ作成されます。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "S_FA-ZvxuXQV" + }, + "source": [ + "## 手動で重みを保存する\n", + "\n", + "上記では重みをモデルにロードする方法を見ました。\n", + "\n", + "手動で重みを保存するのも同じ様に簡単です。`Model.save_weights` メソッドを使います。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "R7W5plyZ-u9X", + "colab": {} + }, + "source": [ + "# 重みの保存\n", + "model.save_weights('./checkpoints/my_checkpoint')\n", + "\n", + "# 重みの復元\n", + "model = create_model()\n", + "model.load_weights('./checkpoints/my_checkpoint')\n", + "\n", + "loss,acc = model.evaluate(test_images, test_labels)\n", + "print(\"Restored model, accuracy: {:5.2f}%\".format(100*acc))" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "kOGlxPRBEvV1" + }, + "source": [ + "## モデル全体の保存\n", + "\n", + "モデルとオプティマイザを、その状態(重みと変数)とモデルの設定の両方を含む1つのファイルに保存することができます。これにより、モデルをオリジナルのPythonコードにアクセスしなくとも使用できるようにエクスポートできます。オプティマイザの状態が復元されるので、中断したところから訓練を再開することも可能です。\n", + "\n", + "完全に機能するモデルを保存できるのは便利です。保存したモデルをTensorFlow.js ([HDF5](https://js.tensorflow.org/tutorials/import-keras.html), [Saved Model](https://js.tensorflow.org/tutorials/import-saved-model.html))でロードし、ブラウザで訓練したり、実行したりすることができるほか、TensorFlow Lite ([HDF5](https://www.tensorflow.org/lite/convert/python_api#exporting_a_tfkeras_file_), [Saved Model](https://www.tensorflow.org/lite/convert/python_api#exporting_a_savedmodel_))\n", + "を使ってモバイルデバイスで実行できるように変換することも可能です。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "SkGwf-50zLNn" + }, + "source": [ + "### HDF5ファイルとして\n", + "\n", + "Kerasでは、[HDF5](https://en.wikipedia.org/wiki/Hierarchical_Data_Format) 標準を使った基本的なファイルフォーマットが利用できます。ここでの利用目的では、保存されたモデルは単独のバイナリラージオブジェクト(blob)として扱うことができます。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "m2dkmJVCGUia", + "colab": {} + }, + "source": [ + "model = create_model()\n", + "\n", + "model.fit(train_images, train_labels, epochs=5)\n", + "\n", + "# モデル全体を1つのHDF5ファイルに保存します。\n", + "model.save('my_model.h5')" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "GWmttMOqS68S" + }, + "source": [ + "保存したファイルを使ってモデルを再作成します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "5NDMO_7kS6Do", + "colab": {} + }, + "source": [ + "# 重みとオプティマイザを含む全く同じモデルを再作成\n", + "new_model = keras.models.load_model('my_model.h5')\n", + "new_model.summary()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "JXQpbTicTBwt" + }, + "source": [ + "正解率を検査します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "jwEaj9DnTCVA", + "colab": {} + }, + "source": [ + "loss, acc = new_model.evaluate(test_images, test_labels)\n", + "print(\"Restored model, accuracy: {:5.2f}%\".format(100*acc))" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "dGXqd4wWJl8O" + }, + "source": [ + "この方法では、次のすべてが保存されます。\n", + "\n", + "* 重みの値\n", + "* モデルの設定(アーキテクチャ)\n", + "* オプティマイザの設定\n", + "\n", + "Kerasは保存する際にアーキテクチャを調べます。いまのところ、TensorFlowのオプティマイザ(`tf.train`に含まれるもの)を保存することはできません。TensorFlowのオプティマイザを使用している場合には、モデルをロードしたあと再コンパイルする必要があり、オプティマイザの状態は失われます。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "kPyhgcoVzqUB" + }, + "source": [ + "### `saved_model`として" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "LtcN4VIb7JkK" + }, + "source": [ + "注意:この手法による`tf.keras`モデルの保存は実験的なもので、将来のバージョンで変更される可能性があります。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "DSWiSB0Q8c46" + }, + "source": [ + "新しいモデルを作ります。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "sI1YvCDFzpl3", + "colab": {} + }, + "source": [ + "model = create_model()\n", + "\n", + "model.fit(train_images, train_labels, epochs=5)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "iUvT_3qE8hV5" + }, + "source": [ + "`saved_model`を作成し、タイムスタンプ付きのディレクトリに保存します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "sq8fPglI1RWA", + "scrolled": true, + "colab": {} + }, + "source": [ + "import time\n", + "saved_model_path = \"./saved_models/{}\".format(int(time.time()))\n", + "\n", + "tf.keras.experimental.export_saved_model(model, saved_model_path)\n", + "saved_model_path" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "MjpmyPfh8-1n" + }, + "source": [ + "作成したsaved_modelsを一覧表示します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "ZtOvxA7V0iTv", + "colab": {} + }, + "source": [ + "!ls saved_models/" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "B7qfpvpY9HCe" + }, + "source": [ + "保存されたモデル(SavedModel)から新しいKerasモデルをリロードします。" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "0YofwHdN0pxa", + "colab": {} + }, + "source": [ + "new_model = tf.keras.experimental.load_from_saved_model(saved_model_path)\n", + "new_model.summary()" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "uWwgNaz19TH2" + }, + "source": [ + "復元されたモデルを実行します。" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "utcL5bs0aoih", + "colab_type": "code", + "colab": {} + }, + "source": [ + "model.predict(test_images).shape" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "Pc9e6G6w1AWG", + "scrolled": true, + "colab": {} + }, + "source": [ + "# モデルを評価する前にコンパイルする必要があります。\n", + "# モデルをデプロイするだけであればこのステップは不要です。\n", + "\n", + "new_model.compile(optimizer=model.optimizer, # ロードしてあったオプティマイザを保持\n", + " loss='sparse_categorical_crossentropy',\n", + " metrics=['accuracy'])\n", + "\n", + "# モデルを評価します。\n", + "loss, acc = new_model.evaluate(test_images, test_labels)\n", + "print(\"Restored model, accuracy: {:5.2f}%\".format(100*acc))" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "eUYTzSz5VxL2" + }, + "source": [ + "## この先は?\n", + "\n", + "`tf.keras`を使った保存とロードのクイックガイドでした。\n", + "\n", + "* [tf.keras guide](https://www.tensorflow.org/guide/keras) には`tf.keras`での保存とロードについて、もう少し記載されています\n", + "\n", + "* Eager Executionでの保存については[Saving in eager](https://www.tensorflow.org/guide/eager#object_based_saving) を参照ください\n", + "\n", + "* [Save and Restore](https://www.tensorflow.org/guide/saved_model)ガイドには、TensorFlowでの保存についてローレベルの詳細が記載されています" + ] + } + ] +} \ No newline at end of file