ホーム » 「TensorFlow 2.0 Tutorials (Beta)」タグがついた投稿

タグアーカイブ: TensorFlow 2.0 Tutorials (Beta)

TensorFlow 2.0 Beta : Tutorials : テキストとシークエンス :- 映画レビューでテキスト分類

TensorFlow 2.0 Beta : Beginner Tutorials : テキストとシークエンス :- 映画レビューでテキスト分類 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 07/01/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Beta – Beginner Tutorials – Text and sequences の以下のページを翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

テキストとシークエンス :- 映画レビューでテキスト分類

このノートブックは (映画) レビューのテキストを使用して映画レビューを肯定的か否定的として分類します。これは二値 — あるいは 2 クラス — 分類の例で、重要で広く利用可能な種類の機械学習問題です。

私達は IMDB データセット を使用します、これは Internet Movie Database からの 50,000 映画レビューのテキストを含みます。これらは訓練のための 25,000 レビューとテストのための 25,000 レビューに分割されます。訓練とテストセットは均等です、つまりそれらがポジティブとネガティブ・レビューの同じ数を含むことを意味します。

この notebook は tf.keras を使用します、TensorFlow でモデルを構築して訓練するための高位 API です。tf.keras を使用したより進んだテキスト分類チュートリアルについては、MLCC テキスト分類ガイド を見てください。

from __future__ import absolute_import, division, print_function, unicode_literals

!pip install -q tensorflow==2.0.0-beta1
import tensorflow as tf
from tensorflow import keras

import numpy as np

print(tf.__version__)
2.0.0-beta1

 

IMDB データセットをダウンロードする

IMDB データセットは TensorFlow でパッケージ化されています。それは既にレビュー (単語のシークエンス) が整数のシークエンスに変換されるように前処理されていて、そこでは各整数は辞書の特定の単語を表しています。

次のコードは IMDB データセットを貴方のマシンにダウンロードします (あるいは既にそれをダウンロードしているのであればキャッシュされたコピーを使用します) :

imdb = keras.datasets.imdb

(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/imdb.npz
17465344/17464789 [==============================] - 0s 0us/step

引数 num_words=10000 は訓練データにおいて最も頻度高く出現する top 10,000 の単語を保持します。データのサイズを管理可能に保つために稀な単語は捨てられます。

 

データを調査する

データのフォーマットを理解するために少し時間をつかいましょう。データセットは前処理されています : 各サンプルは映画レビューの単語を表わす数字の配列です。各ラベルは 0 か 1 の整数値で、そこでは 0 は否定的なレビューで、1 は肯定的なレビューです。

print("Training entries: {}, labels: {}".format(len(train_data), len(train_labels)))
Training entries: 25000, labels: 25000

レビューのテキストは整数に変換され、そこでは各整数は辞書の特定の単語を表します。最初のレビューがどのように見えるかがここにあります :

print(train_data[0])
[1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458, 4468, 66, 3941, 4, 173, 36, 256, 5, 25, 100, 43, 838, 112, 50, 670, 2, 9, 35, 480, 284, 5, 150, 4, 172, 112, 167, 2, 336, 385, 39, 4, 172, 4536, 1111, 17, 546, 38, 13, 447, 4, 192, 50, 16, 6, 147, 2025, 19, 14, 22, 4, 1920, 4613, 469, 4, 22, 71, 87, 12, 16, 43, 530, 38, 76, 15, 13, 1247, 4, 22, 17, 515, 17, 12, 16, 626, 18, 2, 5, 62, 386, 12, 8, 316, 8, 106, 5, 4, 2223, 5244, 16, 480, 66, 3785, 33, 4, 130, 12, 16, 38, 619, 5, 25, 124, 51, 36, 135, 48, 25, 1415, 33, 6, 22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82, 2, 8, 4, 107, 117, 5952, 15, 256, 4, 2, 7, 3766, 5, 723, 36, 71, 43, 530, 476, 26, 400, 317, 46, 7, 4, 2, 1029, 13, 104, 88, 4, 381, 15, 297, 98, 32, 2071, 56, 26, 141, 6, 194, 7486, 18, 4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30, 5535, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226, 65, 16, 38, 1334, 88, 12, 16, 283, 5, 16, 4472, 113, 103, 32, 15, 16, 5345, 19, 178, 32]

映画レビューは異なる長さかもしれません。下のコードは最初と 2 番目のレビューの単語の数を示します。ニューラルネットワークへの入力は同じ長さでなければなりませんので、これを後で解決する必要があります。

len(train_data[0]), len(train_data[1])
(218, 189)

 

整数を単語に変換し戻す

整数をどのようにテキストに変換し戻すかを知ることは有用かもしれません。ここで、整数から文字列へのマッピングを含む辞書オブジェクトに問い合わせるためのヘルパー関数を作成します :

# A dictionary mapping words to an integer index
word_index = imdb.get_word_index()

# The first indices are reserved
word_index = {k:(v+3) for k,v in word_index.items()}
word_index["<PAD>"] = 0
word_index["<START>"] = 1
word_index["<UNK>"] = 2  # unknown
word_index["<UNUSED>"] = 3

reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])

def decode_review(text):
    return ' '.join([reverse_word_index.get(i, '?') for i in text])
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/imdb_word_index.json
1646592/1641221 [==============================] - 0s 0us/step

今では最初のレビューのテキストを表示するために decode_review 関数を使用することができます :

decode_review(train_data[0])
"<START> this film was just brilliant casting location scenery story direction everyone's really suited the part they played and you could just imagine being there robert <UNK> is an amazing actor and now the same being director <UNK> father came from the same scottish island as myself so i loved the fact there was a real connection with this film the witty remarks throughout the film were great it was just brilliant so much that i bought the film as soon as it was released for <UNK> and would recommend it to everyone to watch and the fly fishing was amazing really cried at the end it was so sad and you know what they say if you cry at a film it must have been good and this definitely was also <UNK> to the two little boy's that played the <UNK> of norman and paul they were just brilliant children are often left out of the <> list i think because the stars that play them all grown up are such a big profile for the whole film but these children are amazing and should be praised for what they have done don't you think the whole story was so lovely because it was true and was someone's life after all that was shared with us all"

 

データを準備する

レビュー — 整数の配列 — はニューラルネットワークに供給される前に tensor に変換されなければなりません。この変換は 2 つの方法で成されます :

  • 配列を単語の出現を示す 0 と 1 のベクトルに変換します、one-hot エンコーディングに類似しています。例えば、シークエンス [3, 5] は、(1 である) インデックス 3 と 5 を除いて総てゼロの 10,000 次元ベクトルになるでしょう。それから、これをネットワークの最初の層 — Dense 層 — にします、これは浮動小数点ベクトルデータを処理できます。けれども、このアプローチはメモリ集約的で、num_words * num_reviews サイズ行列を必要とします。
  • 代わりに、配列をそれらが総て同じ長さを持つようにパッドすることもできます、それから shape num_examples * max_length の整数 tensor を作成します。この shape をネットワークの最初の層として扱う埋め込み層を使用できます。

このチュートリアルでは、2 番目のアプローチを使用します。

映画レビューは同じ長さでなければならないので、長さを標準化するために pad_sequences 関数を使用します :

train_data = keras.preprocessing.sequence.pad_sequences(train_data,
                                                        value=word_index[""],
                                                        padding='post',
                                                        maxlen=256)

test_data = keras.preprocessing.sequence.pad_sequences(test_data,
                                                       value=word_index[""],
                                                       padding='post',
                                                       maxlen=256)

さてサンプルの長さを見てみましょう :

len(train_data[0]), len(train_data[1])
(256, 256)

そして (今はパッドされた) 最初のレビューを調査します :

print(train_data[0])
[   1   14   22   16   43  530  973 1622 1385   65  458 4468   66 3941
    4  173   36  256    5   25  100   43  838  112   50  670    2    9
   35  480  284    5  150    4  172  112  167    2  336  385   39    4
  172 4536 1111   17  546   38   13  447    4  192   50   16    6  147
 2025   19   14   22    4 1920 4613  469    4   22   71   87   12   16
   43  530   38   76   15   13 1247    4   22   17  515   17   12   16
  626   18    2    5   62  386   12    8  316    8  106    5    4 2223
 5244   16  480   66 3785   33    4  130   12   16   38  619    5   25
  124   51   36  135   48   25 1415   33    6   22   12  215   28   77
   52    5   14  407   16   82    2    8    4  107  117 5952   15  256
    4    2    7 3766    5  723   36   71   43  530  476   26  400  317
   46    7    4    2 1029   13  104   88    4  381   15  297   98   32
 2071   56   26  141    6  194 7486   18    4  226   22   21  134  476
   26  480    5  144   30 5535   18   51   36   28  224   92   25  104
    4  226   65   16   38 1334   88   12   16  283    5   16 4472  113
  103   32   15   16 5345   19  178   32    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0]

 

モデルを構築する

ニューラルネットワークは層をスタックすることにより作成されます — これは 2 つの主要なアーキテクチャ的な決定を必要とします :

  • モデルで幾つの層を使用するか?
  • 各層のために幾つの隠れユニットを使用するか?

このサンプルでは、入力データは単語インデックスの配列から成ります。予測するラベルは 0 か 1 です。この問題に対するモデルを構築しましょう :

# input shape is the vocabulary count used for the movie reviews (10,000 words)
vocab_size = 10000

model = keras.Sequential()
model.add(keras.layers.Embedding(vocab_size, 16))
model.add(keras.layers.GlobalAveragePooling1D())
model.add(keras.layers.Dense(16, activation='relu'))
model.add(keras.layers.Dense(1, activation='sigmoid'))

model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding (Embedding)        (None, None, 16)          160000    
_________________________________________________________________
global_average_pooling1d (Gl (None, 16)                0         
_________________________________________________________________
dense (Dense)                (None, 16)                272       
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 17        
=================================================================
Total params: 160,289
Trainable params: 160,289
Non-trainable params: 0
_________________________________________________________________

分類器を構築するために層はシーケンシャルにスタックされます :

  1. 最初の層は Embedding 層です。この層は整数エンコードされた語彙を取って各単語インデックスのための埋め込みベクトルを検索します。これらのベクトルはモデルが訓練されるときに学習されます。ベクトルは出力配列に次元を追加します。結果としての次元は (batch, sequence, embedding) です。
  2. 次に、GlobalAveragePooling1D 層は各サンプルについて sequence 次元に渡り平均することにより固定長出力ベクトルを返します。これは可能な最も単純な方法でモデルが可変長の入力を扱うことを可能にします。
  3. この固定長出力ベクトルは 16 隠れユニットを持つ完全結合 (Dense) 層を通してパイプされます。
  4. 最後の層は単一の出力ノードに密に接続されています。sigmoid 活性化関数を使用し、この値は 0 と 1 の間の浮動小数点で、確率、または確信レベルを表します。

 

隠れユニット

上のモデルは入力と出力の間に、2 つの中間層あるいは「隠れ」層を持ちます。出力 (ユニット、ノード、またはニューロン) の数は層のための具象空間の次元です。換言すれば、内部表現を学習するときにネットワークが許容される自由度の総量です。

モデルがより多くの隠れユニット (より高い次元の表現空間) and/or より多くの層を持てば、ネットワークはより複雑な表現を学習できます。けれども、それはネットワークをより計算的に高価にして望まないパターンを学習することに繋がるかもしれません — このパターンは訓練データ上の性能を改善しますがテストデータ上ではそうではないものです。これは overfitting と呼ばれ、後でそれを調査します。

 

損失関数と optimizer

モデルは訓練のために損失関数と optimizer を必要とします。これは二値分類問題でモデルは確率を出力します (sigmoid 活性を持つシングルユニット層) ので、binary_crossentropy 損失関数を使用します。

これは損失関数のための唯一の選択ではありません、例えば、mean_squared_error を選択できるでしょう。しかし、一般的に、binary_crossentropy は確率を扱うためにはより良いです — それは確率分布間、あるいは私達のケースでは、正解の分布と予測の間の「距離」を測ります。

後で、回帰問題 (例えば、家の価格を予測する) を調べているときに、mean squared error と呼ばれるもう一つの損失関数をどのように使用するかを見ます。

さて、optimizer と損失関数を使用するためにモデルを configure します :

model.compile(optimizer='adam',
              loss='binary_crossentropy',
              metrics=['accuracy'])

 

検証セットを作成する

訓練時、モデルの精度をそれが前に見ていないデータ上でチェックすることを望みます。元の訓練データから 10,000 サンプルを分離することによって検証セットを作成します。(テストセットを何故今使わないのでしょう?目標は訓練データのみを用いてモデルを開発して調整することですから、テストデータは精度を評価するためだけに一度だけ使用します。)

x_val = train_data[:10000]
partial_x_train = train_data[10000:]

y_val = train_labels[:10000]
partial_y_train = train_labels[10000:]

 

モデルを訓練する

モデルを 512 サンプルのミニバッチで 40 エポック訓練します。これは x_train と y_train tensor の総てのサンプルに渡る 40 iteration (反復) です。訓練の間、検証セットからの 10,000 サンプル上でモデルの損失と精度を監視します :

history = model.fit(partial_x_train,
                    partial_y_train,
                    epochs=40,
                    batch_size=512,
                    validation_data=(x_val, y_val),
                    verbose=1)
WARNING: Logging before flag parsing goes to stderr.
W0628 04:24:41.652144 139848380724992 deprecation.py:323] From /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/ops/math_grad.py:1250: add_dispatch_support..wrapper (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.where in 2.0, which has the same broadcast rule as np.where

Train on 15000 samples, validate on 10000 samples
Epoch 1/40
15000/15000 [==============================] - 1s 72us/sample - loss: 0.6923 - accuracy: 0.5162 - val_loss: 0.6910 - val_accuracy: 0.5289
Epoch 2/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.6879 - accuracy: 0.5706 - val_loss: 0.6849 - val_accuracy: 0.5642
Epoch 3/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.6778 - accuracy: 0.6386 - val_loss: 0.6721 - val_accuracy: 0.6729
Epoch 4/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.6591 - accuracy: 0.6863 - val_loss: 0.6503 - val_accuracy: 0.7282
Epoch 5/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.6304 - accuracy: 0.7521 - val_loss: 0.6191 - val_accuracy: 0.7656
Epoch 6/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.5897 - accuracy: 0.8036 - val_loss: 0.5782 - val_accuracy: 0.7876
Epoch 7/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.5414 - accuracy: 0.8253 - val_loss: 0.5318 - val_accuracy: 0.8198
Epoch 8/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.4918 - accuracy: 0.8471 - val_loss: 0.4883 - val_accuracy: 0.8327
Epoch 9/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.4452 - accuracy: 0.8619 - val_loss: 0.4489 - val_accuracy: 0.8435
Epoch 10/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.4037 - accuracy: 0.8742 - val_loss: 0.4163 - val_accuracy: 0.8519
Epoch 11/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.3685 - accuracy: 0.8827 - val_loss: 0.3894 - val_accuracy: 0.8598
Epoch 12/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.3391 - accuracy: 0.8911 - val_loss: 0.3689 - val_accuracy: 0.8631
Epoch 13/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.3150 - accuracy: 0.8971 - val_loss: 0.3510 - val_accuracy: 0.8699
Epoch 14/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.2937 - accuracy: 0.9024 - val_loss: 0.3380 - val_accuracy: 0.8729
Epoch 15/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.2760 - accuracy: 0.9074 - val_loss: 0.3270 - val_accuracy: 0.8744
Epoch 16/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.2603 - accuracy: 0.9125 - val_loss: 0.3184 - val_accuracy: 0.8755
Epoch 17/40
15000/15000 [==============================] - 1s 52us/sample - loss: 0.2459 - accuracy: 0.9171 - val_loss: 0.3111 - val_accuracy: 0.8786
Epoch 18/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.2331 - accuracy: 0.9219 - val_loss: 0.3048 - val_accuracy: 0.8807
Epoch 19/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.2217 - accuracy: 0.9238 - val_loss: 0.2998 - val_accuracy: 0.8809
Epoch 20/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.2114 - accuracy: 0.9277 - val_loss: 0.2964 - val_accuracy: 0.8820
Epoch 21/40
15000/15000 [==============================] - 1s 51us/sample - loss: 0.2009 - accuracy: 0.9331 - val_loss: 0.2935 - val_accuracy: 0.8829
Epoch 22/40
15000/15000 [==============================] - 1s 51us/sample - loss: 0.1922 - accuracy: 0.9361 - val_loss: 0.2905 - val_accuracy: 0.8844
Epoch 23/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.1834 - accuracy: 0.9408 - val_loss: 0.2895 - val_accuracy: 0.8835
Epoch 24/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.1758 - accuracy: 0.9441 - val_loss: 0.2885 - val_accuracy: 0.8840
Epoch 25/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.1681 - accuracy: 0.9469 - val_loss: 0.2866 - val_accuracy: 0.8855
Epoch 26/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.1611 - accuracy: 0.9498 - val_loss: 0.2875 - val_accuracy: 0.8854
Epoch 27/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.1546 - accuracy: 0.9519 - val_loss: 0.2868 - val_accuracy: 0.8853
Epoch 28/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.1483 - accuracy: 0.9549 - val_loss: 0.2879 - val_accuracy: 0.8849
Epoch 29/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.1428 - accuracy: 0.9575 - val_loss: 0.2888 - val_accuracy: 0.8845
Epoch 30/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.1371 - accuracy: 0.9593 - val_loss: 0.2887 - val_accuracy: 0.8871
Epoch 31/40
15000/15000 [==============================] - 1s 51us/sample - loss: 0.1312 - accuracy: 0.9619 - val_loss: 0.2896 - val_accuracy: 0.8862
Epoch 32/40
15000/15000 [==============================] - 1s 51us/sample - loss: 0.1261 - accuracy: 0.9649 - val_loss: 0.2912 - val_accuracy: 0.8862
Epoch 33/40
15000/15000 [==============================] - 1s 50us/sample - loss: 0.1211 - accuracy: 0.9659 - val_loss: 0.2935 - val_accuracy: 0.8851
Epoch 34/40
15000/15000 [==============================] - 1s 48us/sample - loss: 0.1166 - accuracy: 0.9670 - val_loss: 0.2960 - val_accuracy: 0.8847
Epoch 35/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.1124 - accuracy: 0.9683 - val_loss: 0.2980 - val_accuracy: 0.8855
Epoch 36/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.1079 - accuracy: 0.9699 - val_loss: 0.2998 - val_accuracy: 0.8840
Epoch 37/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.1036 - accuracy: 0.9721 - val_loss: 0.3027 - val_accuracy: 0.8838
Epoch 38/40
15000/15000 [==============================] - 1s 48us/sample - loss: 0.0996 - accuracy: 0.9734 - val_loss: 0.3062 - val_accuracy: 0.8832
Epoch 39/40
15000/15000 [==============================] - 1s 48us/sample - loss: 0.0965 - accuracy: 0.9743 - val_loss: 0.3101 - val_accuracy: 0.8823
Epoch 40/40
15000/15000 [==============================] - 1s 49us/sample - loss: 0.0922 - accuracy: 0.9762 - val_loss: 0.3129 - val_accuracy: 0.8825

 

モデルを評価する

そしてモデルどのように遂行するか見ましょう。2 つの値が返されます。損失 (エラーを表わす数字です、より低ければより良いです)、そして精度です。

results = model.evaluate(test_data, test_labels)

print(results)
25000/25000 [==============================] - 1s 35us/sample - loss: 0.3338 - accuracy: 0.8712
[0.3338048003101349, 0.87124]

このかなり素朴なアプローチは約 87 % の精度を得ます。より進んだアプローチでは、モデルは 95 % に近づくはずです。

 

時間とともに精度と損失のグラフを作成する

model.fit() は History オブジェクトを返します、これは訓練の間に発生した総てを持つ辞書を含みます :

history_dict = history.history
history_dict.keys()
dict_keys(['val_loss', 'accuracy', 'val_accuracy', 'loss'])

4 つのエントリがあります: 訓練と検証の間に各々監視されたメトリックのために一つ (ずつ) です。比較のために訓練と検証精度に加えて、訓練と検証損失をプロットするためにこれらを使用することができます :

import matplotlib.pyplot as plt

acc = history_dict['accuracy']
val_acc = history_dict['val_accuracy']
loss = history_dict['loss']
val_loss = history_dict['val_loss']

epochs = range(1, len(acc) + 1)

# "bo" is for "blue dot"
plt.plot(epochs, loss, 'bo', label='Training loss')
# b is for "solid blue line"
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()

plt.show()
<Figure size 640x480 with 1 Axes>
plt.clf()   # clear figure

plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()

plt.show()

このプロットでは、点線は訓練損失と精度を表し、そして実線は検証損失と精度です。

訓練損失は各エポックとともに減少して訓練精度は各エポックとともに増加することに気がつくでしょう。これは勾配降下最適化を使用するときに期待されるものです — それは総ての反復で望まれる量を最小化するはずです。

これは検証損失と精度については当てはまりません — それらは約 20 エポック後に最大になるようです。これは overfitting の例です : モデルは、それが前に決して見ていないデータ上よりも訓練データ上でより上手く遂行します。このポイント後、モデルは過剰に最適化されてテストデータに一般化されない訓練データに固有の表現を学習します。

この特定のケースのためには、単純に 20 程度のエポック後に訓練を停止することで overfitting を回避できるでしょう。後で、これを callback で自動的にどのように行なうかを見るでしょう。

 

以上



TensorFlow 2.0 Beta : Tutorials : テキストとシークエンス :- RNN でテキスト分類

TensorFlow 2.0 Beta : Beginner Tutorials : テキストとシークエンス :- RNN でテキスト分類 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 07/01/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Beta – Beginner Tutorials – Text and sequences の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

テキストとシークエンス :- RNN でテキスト分類

このテキスト分類チュートリアルはセンチメント分析のために IMDB 巨大映画レビューデータセット 上で リカレント・ニューラルネットワーク を訓練します。

from __future__ import absolute_import, division, print_function, unicode_literals

!pip install -q tensorflow-gpu==2.0.0-beta1
import tensorflow_datasets as tfds
import tensorflow as tf

matplotlib をインポートしてグラフをプロットするためのヘルパー関数を作成します :

import matplotlib.pyplot as plt


def plot_graphs(history, string):
  plt.plot(history.history[string])
  plt.plot(history.history['val_'+string])
  plt.xlabel("Epochs")
  plt.ylabel(string)
  plt.legend([string, 'val_'+string])
  plt.show()

 

入力パイプラインをセットアップする

IMDB 巨大映画レビューデータセットは二値分類データセットです — 総てのレビューはポジティブかネガティブなセンチメントを持ちます。

TFDS を使用してデータセットをダウンロードします。データセットは作り付けの部分語字句解析器 (= subword tokenizer) を装備しています。

dataset, info = tfds.load('imdb_reviews/subwords8k', with_info=True,
                          as_supervised=True)
train_dataset, test_dataset = dataset['train'], dataset['test']
Downloading and preparing dataset imdb_reviews (80.23 MiB) to /home/kbuilder/tensorflow_datasets/imdb_reviews/subwords8k/0.1.0...

HBox(children=(IntProgress(value=1, bar_style='info', description='Dl Completed...', max=1, style=ProgressStyl…
HBox(children=(IntProgress(value=1, bar_style='info', description='Dl Size...', max=1, style=ProgressStyle(des…




HBox(children=(IntProgress(value=1, bar_style='info', max=1), HTML(value='')))


HBox(children=(IntProgress(value=0, description='Shuffling...', max=10, style=ProgressStyle(description_width=…
WARNING: Logging before flag parsing goes to stderr.
W0628 05:40:53.455971 139635681199872 deprecation.py:323] From /home/kbuilder/.local/lib/python3.5/site-packages/tensorflow_datasets/core/file_format_adapter.py:209: tf_record_iterator (from tensorflow.python.lib.io.tf_record) is deprecated and will be removed in a future version.
Instructions for updating:
Use eager execution and: 
<a href="../../../versions/r2.0/api_docs/python/tf/data/TFRecordDataset"><code>tf.data.TFRecordDataset(path)</code></a>

HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…


HBox(children=(IntProgress(value=1, bar_style='info', max=1), HTML(value='')))


HBox(children=(IntProgress(value=0, description='Shuffling...', max=10, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…


HBox(children=(IntProgress(value=1, bar_style='info', max=1), HTML(value='')))


HBox(children=(IntProgress(value=0, description='Shuffling...', max=20, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des…
HBox(children=(IntProgress(value=0, description='Writing...', max=2500, style=ProgressStyle(description_width=…
Dataset imdb_reviews downloaded and prepared to /home/kbuilder/tensorflow_datasets/imdb_reviews/subwords8k/0.1.0. Subsequent calls will reuse this data.

 
これは部分語字句解析器ですから、それは任意の文字列を渡すことができて字句解析器はそれをトークン化します。

tokenizer = info.features['text'].encoder
print ('Vocabulary size: {}'.format(tokenizer.vocab_size))
Vocabulary size: 8185
sample_string = 'TensorFlow is cool.'

tokenized_string = tokenizer.encode(sample_string)
print ('Tokenized string is {}'.format(tokenized_string))

original_string = tokenizer.decode(tokenized_string)
print ('The original string: {}'.format(original_string))

assert original_string == sample_string
Tokenized string is [6307, 2327, 4043, 4265, 9, 2724, 7975]
The original string: TensorFlow is cool.

字句解析器は単語がその辞書にない場合には文字列を部分語に分解してエンコードします。

for ts in tokenized_string:
  print ('{} ----> {}'.format(ts, tokenizer.decode([ts])))
6307 ----> Ten
2327 ----> sor
4043 ----> Fl
4265 ----> ow 
9 ----> is 
2724 ----> cool
7975 ----> .
BUFFER_SIZE = 10000
BATCH_SIZE = 64
train_dataset = train_dataset.shuffle(BUFFER_SIZE)
train_dataset = train_dataset.padded_batch(BATCH_SIZE, train_dataset.output_shapes)

test_dataset = test_dataset.padded_batch(BATCH_SIZE, test_dataset.output_shapes)

 

モデルを作成する

tf.keras.Sequential モデルを構築して埋め込み層から始めます。埋め込み層は単語毎に一つのベクトルをストアします。呼び出されたとき、それは単語インデックスのシークエンスをベクトルのシークエンスに変換します。これらのベクトルは訓練可能です。(十分なデータ上で) 訓練後、類似の意味を持つ単語はしばしば同様のベクトルを持ちます。

このインデックス検索は tf.keras.layers.Dense 層を通した one-hot エンコード・ベクトルを渡す等値の演算よりも遥かにより効率的です。

リカレント・ニューラルネットワーク (RNN) は要素を通した iterate によるシークエンス入力を処理します。RNN は一つの時間ステップからの出力をそれらの入力 — そして次へと渡します。

tf.keras.layers.Bidirectional ラッパーはまた RNN 層とともに使用できます。これは RNN 層を通して入力を foward そして backward に伝播してそれから出力を連結します。これは RNN が長期の (= long range) 依存性を学習する手助けをします。

model = tf.keras.Sequential([
    tf.keras.layers.Embedding(tokenizer.vocab_size, 64),
    tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(64)),
    tf.keras.layers.Dense(64, activation='relu'),
    tf.keras.layers.Dense(1, activation='sigmoid')
])

訓練プロセスを構成するために Keras モデルをコンパイルします :

model.compile(loss='binary_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])

 

モデルを訓練する

history = model.fit(train_dataset, epochs=10,
                    validation_data=test_dataset)
Epoch 1/10

W0628 05:42:47.879820 139635681199872 deprecation.py:323] From /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/ops/math_grad.py:1250: add_dispatch_support..wrapper (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.where in 2.0, which has the same broadcast rule as np.where

391/391 [==============================] - 367s 939ms/step - loss: 0.5840 - accuracy: 0.6862 - val_loss: 0.0000e+00 - val_accuracy: 0.0000e+00
Epoch 2/10
391/391 [==============================] - 138s 352ms/step - loss: 0.4049 - accuracy: 0.8229 - val_loss: 0.4430 - val_accuracy: 0.7974
Epoch 3/10
391/391 [==============================] - 102s 262ms/step - loss: 0.3484 - accuracy: 0.8620 - val_loss: 0.4643 - val_accuracy: 0.7994
Epoch 4/10
391/391 [==============================] - 98s 250ms/step - loss: 0.3774 - accuracy: 0.8357 - val_loss: 0.4392 - val_accuracy: 0.8188
Epoch 5/10
391/391 [==============================] - 86s 219ms/step - loss: 0.2587 - accuracy: 0.9017 - val_loss: 0.4777 - val_accuracy: 0.7760
Epoch 6/10
391/391 [==============================] - 81s 208ms/step - loss: 0.2229 - accuracy: 0.9178 - val_loss: 0.4470 - val_accuracy: 0.8356
Epoch 7/10
391/391 [==============================] - 81s 206ms/step - loss: 0.1814 - accuracy: 0.9357 - val_loss: 0.5000 - val_accuracy: 0.8382
Epoch 8/10
391/391 [==============================] - 76s 193ms/step - loss: 0.2660 - accuracy: 0.8936 - val_loss: 0.5101 - val_accuracy: 0.8174
Epoch 9/10
391/391 [==============================] - 80s 204ms/step - loss: 0.2428 - accuracy: 0.9040 - val_loss: 0.5396 - val_accuracy: 0.8051
Epoch 10/10
391/391 [==============================] - 75s 191ms/step - loss: 0.2763 - accuracy: 0.8853 - val_loss: 0.6413 - val_accuracy: 0.6517
test_loss, test_acc = model.evaluate(test_dataset)

print('Test Loss: {}'.format(test_loss))
print('Test Accuracy: {}'.format(test_acc))
    391/Unknown - 20s 51ms/step - loss: 0.6413 - accuracy: 0.6517Test Loss: 0.6412561183695293
Test Accuracy: 0.6516799926757812

上のモデルはシークエンスに適用されるパディングをマスクしていません。これはパッドされたシークエンス上で訓練してパッドされていないシークエンス上でテストする場合に歪みに繋がる可能性があります。理想的にはモデルはパディングを無視することを学習するでしょうが、下で見れるようにそれは出力上で小さい効果を持つだけです。

prediction が >= 0.5 であれば、それはポジティブでそうでなければネガティブです。

def pad_to_size(vec, size):
  zeros = [0] * (size - len(vec))
  vec.extend(zeros)
  return vec
def sample_predict(sentence, pad):
  tokenized_sample_pred_text = tokenizer.encode(sample_pred_text)

  if pad:
    tokenized_sample_pred_text = pad_to_size(tokenized_sample_pred_text, 64)

  predictions = model.predict(tf.expand_dims(tokenized_sample_pred_text, 0))

  return (predictions)
# predict on a sample text without padding.

sample_pred_text = ('The movie was cool. The animation and the graphics '
                    'were out of this world. I would recommend this movie.')
predictions = sample_predict(sample_pred_text, pad=False)
print (predictions)
[[0.63414526]]
# predict on a sample text with padding

sample_pred_text = ('The movie was cool. The animation and the graphics '
                    'were out of this world. I would recommend this movie.')
predictions = sample_predict(sample_pred_text, pad=True)
print (predictions)
[[0.66387755]]
plot_graphs(history, 'accuracy')

plot_graphs(history, 'loss')

 

2 つあるいはそれ以上の LSTM 層をスタックする

Keras リカレント層は return_sequences コンストラクタ引数で制御される 2 つの利用可能なモードを持ちます :

  • 各タイムスタンプのための連続する出力の完全なシークエンスを返すか (shape (batch_size, timesteps, output_features) の 3D tensor)、
  • 各入力シークエンスのための最後の出力だけを返します (shape (batch_size, output_features) の 2D tensor)。
model = tf.keras.Sequential([
    tf.keras.layers.Embedding(tokenizer.vocab_size, 64),
    tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(
        64, return_sequences=True)),
    tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(32)),
    tf.keras.layers.Dense(64, activation='relu'),
    tf.keras.layers.Dense(1, activation='sigmoid')
])
model.compile(loss='binary_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])
history = model.fit(train_dataset, epochs=10,
                    validation_data=test_dataset)
Epoch 1/10
391/391 [==============================] - 710s 2s/step - loss: 0.6758 - accuracy: 0.5653 - val_loss: 0.0000e+00 - val_accuracy: 0.0000e+00
Epoch 2/10
391/391 [==============================] - 240s 615ms/step - loss: 0.6268 - accuracy: 0.6446 - val_loss: 0.5156 - val_accuracy: 0.7750
Epoch 3/10
391/391 [==============================] - 185s 473ms/step - loss: 0.3965 - accuracy: 0.8324 - val_loss: 0.4141 - val_accuracy: 0.8158
Epoch 4/10
391/391 [==============================] - 166s 424ms/step - loss: 0.3237 - accuracy: 0.8726 - val_loss: 0.4115 - val_accuracy: 0.8274
Epoch 5/10
391/391 [==============================] - 156s 398ms/step - loss: 0.2794 - accuracy: 0.8934 - val_loss: 0.3814 - val_accuracy: 0.8395
Epoch 6/10
391/391 [==============================] - 138s 353ms/step - loss: 0.2052 - accuracy: 0.9269 - val_loss: 0.3805 - val_accuracy: 0.8464
Epoch 7/10
391/391 [==============================] - 140s 359ms/step - loss: 0.1538 - accuracy: 0.9493 - val_loss: 0.4158 - val_accuracy: 0.8466
Epoch 8/10
391/391 [==============================] - 130s 334ms/step - loss: 0.1210 - accuracy: 0.9622 - val_loss: 0.4478 - val_accuracy: 0.8453
Epoch 9/10
391/391 [==============================] - 134s 343ms/step - loss: 0.0934 - accuracy: 0.9723 - val_loss: 0.4828 - val_accuracy: 0.8485
Epoch 10/10
391/391 [==============================] - 137s 351ms/step - loss: 0.0735 - accuracy: 0.9798 - val_loss: 0.5393 - val_accuracy: 0.8396
test_loss, test_acc = model.evaluate(test_dataset)

print('Test Loss: {}'.format(test_loss))
print('Test Accuracy: {}'.format(test_acc))
    391/Unknown - 35s 89ms/step - loss: 0.5393 - accuracy: 0.8396Test Loss: 0.5392520240962962
Test Accuracy: 0.8395599722862244
# predict on a sample text without padding.

sample_pred_text = ('The movie was not good. The animation and the graphics '
                    'were terrible. I would not recommend this movie.')
predictions = sample_predict(sample_pred_text, pad=False)
print (predictions)
[[0.00492772]]
# predict on a sample text with padding

sample_pred_text = ('The movie was not good. The animation and the graphics '
                    'were terrible. I would not recommend this movie.')
predictions = sample_predict(sample_pred_text, pad=True)
print (predictions)
[[0.00426524]]
plot_graphs(history, 'accuracy')

plot_graphs(history, 'loss')

GRU 層 のような他の存在するリカレント層を調べてください。

 

以上



TensorFlow 2.0 Beta : Tutorials : テキストとシークエンス :- 単語埋め込み

TensorFlow 2.0 Beta : Beginner Tutorials : テキストとシークエンス :- 単語埋め込み (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 06/30/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Beta – Beginner Tutorials – Text and sequences の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

テキストとシークエンス :- 単語埋め込み

このチュートリアルは単語埋め込みを紹介します。それは小さいデータセット上でスクラッチから単語埋め込みを訓練して埋め込みプロジェクター (= Embedding Projector) (下の画像で示されます) を使用してこれらの埋め込みを可視化する完全なコードを含みます。

 

テキストを数字として表現する

機械学習モデルは入力としてベクトル (数字の配列) を取ります。テキストで作業するとき、行わなければならない最初のことはそれをモデルに供給する前の文字列を数字に変換する (or テキストを「ベクトル化」する) ストラテジーを考えだすことです。このセクションでは、それを行なうための 3 つのストラテジーを見ます。

 

One-hot エンコーディング

最初のアイデアとして、語彙の各単語を “one-hot” エンコードできるかもしれません。センテンス “The cat sat on the mat” を考えます。このセンテンスの語彙 (or 一意の単語) は (cat, mat, on, sat, the) です。各単語を表わすために、語彙に等しい長さを持つゼロベクトルを作成してから、単語に対応するインデックス内に 1 を置きます。このアプローチは次の図で示されます。

センテンスのエンコーディングを含むベクトルを作成するために、それから各単語のための one-hot ベクトルを連結できるでしょう。

Key point: このアプローチは不十分です。one-hot エンコード・ベクトルは疎です (つまり、殆どのインデックスがゼロです)。語彙に 10,000 単語を持つことを想像してください。各単語を one-hot エンコードするには、要素の 99.99% はゼロであるベクトルを作成するでしょう。

 

一意の数字を持つ各単語をエンコードする

私達が試しても良い 2 番目のアプローチは一意の数字を使用して各単語をエンコードすることです。上の例を続けるのであれば、1 を “cat” に、2 を “mat” 等に割り当てることができるでしょう。それからセンテンス “The cat sat on the mat” を [5, 1, 4, 3, 5, 2] のような密ベクトルとしてエンコードできるでしょう。このアプローチは効率的です。疎ベクトルの代わりに、今では密なひとつ (そこでは総ての要素は満ちています) を持ちます。

けれども、このアプローチには 2 つの欠点があります :

  • 整数エンコーディングは恣意的 (= arbitrary) です (それは単語間のどのような関係も捕捉しません)。
  • 整数エンコーディングはモデルが解釈することは挑戦的です。例えば、線形分類器は各特徴について単一の重みを学習します。任意の 2 つの単語の類似性とそれらのエンコーディングの類似性の間に関係はありませんので、この特徴-重みの組み合わせは意味がありません。

 

単語埋め込み

単語埋め込みは効率的で、密な表現を使用する方法を与えてくれます、そこでは類似した単語は類似したエンコーディングを持ちます。重要なことは、このエンコーディングを手動で指定しなくてかまわないことです。埋め込みは浮動小数点値の密ベクトルです (ベクトルの長さは貴方が指定するパラメータです)。埋め込みのための値を手動で指定する代わりに、それらは訓練可能なパラメータです (モデルが dense 層のための重みを学習するのと同じ方法で、訓練の間にモデルにより学習される重みです)。(小さいデータセットのための) 8-次元から、巨大なデータセットで作業するとき 1024 次元までの単語埋め込みを見ることは一般的です。高い次元の埋め込みは単語間のきめ細かい関係性を捕捉できますが、学習するためにより多くのデータを取ります。

上は単語埋め込みのための図です。各単語は浮動小数点値の 4-次元ベクトルとして表現されます。埋め込みを考えるもう一つの方法は「検索テーブル (= lookup table)」です。これらの重みが学習された後、各単語をそれがテーブルで対応する密ベクトルを検索することでエンコードできます。

 

埋め込み層を使用する

Keras は単語埋め込みを使用することを容易にします。Embedding 層を見てみましょう。

from __future__ import absolute_import, division, print_function, unicode_literals

!pip install -q tensorflow==2.0.0-beta1
import tensorflow as tf

from tensorflow import keras
from tensorflow.keras import layers

# The Embedding layer takes at least two arguments:
# the number of possible words in the vocabulary, here 1000 (1 + maximum word index),
# and the dimensionality of the embeddings, here 32.
embedding_layer = layers.Embedding(1000, 32)

Embedding 層は整数インデックス (これは特定の単語を表します) から密ベクトル (それらの埋め込み) へマップする検索テーブルとして理解できます。埋め込みの次元 (or width) は、(Dense 層のニューロン数で実験するのとだいたい同じ方法で) それで貴方の問題のために何が上手く動作するかを見るための実験可能なパラメータです。

埋め込み層を作成するとき、その埋め込めのための重みは (丁度任意の他の層のように) ランダムに初期化されます。訓練の間に、それらは徐々に逆伝播で調整されます。ひとたび訓練されれば、学習された単語埋め込みは単語間の類似性を大雑把にエンコードします (何故ならばそれらは貴方のモデルがその上で訓練された特定の問題のために学習されたからです)。

入力として、Embedding 層は shape (samples, sequence_length) の整数の 2D tensor を取ります、そこでは各エントリは整数のシークエンスです。それは可変長のシークエンスを埋め込むことができます。上の埋め込み層に shapes (32, 10) (長さ 10 の 32 シークエンスのバッチ) あるいは (64, 15) (長さ 15 の 64 シークエンスのバッチ) を持つバッチを供給できるでしょう。バッチの総てのシークエンスは同じ長さを持たなければなりませんので、他よりも短いシークエンスはゼロでパディングされるべきで、より長いシークエンスは切り縮められるべきです。

出力として、埋め込み層は shape (samples, sequence_length, embedding_dimensionality) の 3D 浮動小数点 tensor を返します。そのような 3D tensor はそれから RNN で処理できますし、あるいは単純に平坦化されるかプールされるかして Dense 層で処理できます。このチュートリアルでは後者のアプローチを示します、そして最初のものを学習するために Text Classification with an RNN を参照できます。

 

スクラッチから埋め込みを学習する

IMDB 映画レビュー上でセンチメント分類器を訓練します。この過程で、スクラッチから埋め込みを学習します。データセットをダウンロードして前処理するコードを通して迅速に進めます (より詳細はこの チュートリアル を参照)。

vocab_size = 10000
imdb = keras.datasets.imdb
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=vocab_size)

インポートされたとき、レビューのテキストは整数エンコードされています (各整数は辞書の特定の単語を表します)。

print(train_data[0])
[1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458, 4468, 66, 3941, 4, 173, 36, 256, 5, 25, 100, 43, 838, 112, 50, 670, 2, 9, 35, 480, 284, 5, 150, 4, 172, 112, 167, 2, 336, 385, 39, 4, 172, 4536, 1111, 17, 546, 38, 13, 447, 4, 192, 50, 16, 6, 147, 2025, 19, 14, 22, 4, 1920, 4613, 469, 4, 22, 71, 87, 12, 16, 43, 530, 38, 76, 15, 13, 1247, 4, 22, 17, 515, 17, 12, 16, 626, 18, 2, 5, 62, 386, 12, 8, 316, 8, 106, 5, 4, 2223, 5244, 16, 480, 66, 3785, 33, 4, 130, 12, 16, 38, 619, 5, 25, 124, 51, 36, 135, 48, 25, 1415, 33, 6, 22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82, 2, 8, 4, 107, 117, 5952, 15, 256, 4, 2, 7, 3766, 5, 723, 36, 71, 43, 530, 476, 26, 400, 317, 46, 7, 4, 2, 1029, 13, 104, 88, 4, 381, 15, 297, 98, 32, 2071, 56, 26, 141, 6, 194, 7486, 18, 4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30, 5535, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226, 65, 16, 38, 1334, 88, 12, 16, 283, 5, 16, 4472, 113, 103, 32, 15, 16, 5345, 19, 178, 32]

 

整数を単語に変換し戻す

整数をどのようにテキストに変換し戻すかを知ることは有用かもしれません。ここで、整数から文字列へのマッピングを含む辞書オブジェクトに問い合わせるためのヘルパー関数を作成します :

# A dictionary mapping words to an integer index
word_index = imdb.get_word_index()

# The first indices are reserved
word_index = {k:(v+3) for k,v in word_index.items()}
word_index[""] = 0
word_index[""] = 1
word_index[""] = 2  # unknown
word_index[""] = 3

reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])

def decode_review(text):
    return ' '.join([reverse_word_index.get(i, '?') for i in text])

decode_review(train_data[0])
"<START> this film was just brilliant casting location scenery story direction everyone's really suited the part they played and you could just imagine being there robert <UNK> is an amazing actor and now the same being director <UNK> father came from the same scottish island as myself so i loved the fact there was a real connection with this film the witty remarks throughout the film were great it was just brilliant so much that i bought the film as soon as it was released for <UNK> and would recommend it to everyone to watch and the fly fishing was amazing really cried at the end it was so sad and you know what they say if you cry at a film it must have been good and this definitely was also <UNK> to the two little boy's that played the <UNK> of norman and paul they were just brilliant children are often left out of the <UNK> list i think because the stars that play them all grown up are such a big profile for the whole film but these children are amazing and should be praised for what they have done don't you think the whole story was so lovely because it was true and was someone's life after all that was shared with us all"

映画レビューは異なる長さであり得ます。レビューの長さを標準化するために pad_sequences 関数を使用します。

maxlen = 500

train_data = keras.preprocessing.sequence.pad_sequences(train_data,
                                                        value=word_index[""],
                                                        padding='post',
                                                        maxlen=maxlen)

test_data = keras.preprocessing.sequence.pad_sequences(test_data,
                                                       value=word_index[""],
                                                       padding='post',
                                                       maxlen=maxlen)

最初のパッドされたレビューを調べましょう。

print(train_data[0])
[   1   14   22   16   43  530  973 1622 1385   65  458 4468   66 3941
    4  173   36  256    5   25  100   43  838  112   50  670    2    9
   35  480  284    5  150    4  172  112  167    2  336  385   39    4
  172 4536 1111   17  546   38   13  447    4  192   50   16    6  147
 2025   19   14   22    4 1920 4613  469    4   22   71   87   12   16
   43  530   38   76   15   13 1247    4   22   17  515   17   12   16
  626   18    2    5   62  386   12    8  316    8  106    5    4 2223
 5244   16  480   66 3785   33    4  130   12   16   38  619    5   25
  124   51   36  135   48   25 1415   33    6   22   12  215   28   77
   52    5   14  407   16   82    2    8    4  107  117 5952   15  256
    4    2    7 3766    5  723   36   71   43  530  476   26  400  317
   46    7    4    2 1029   13  104   88    4  381   15  297   98   32
 2071   56   26  141    6  194 7486   18    4  226   22   21  134  476
   26  480    5  144   30 5535   18   51   36   28  224   92   25  104
    4  226   65   16   38 1334   88   12   16  283    5   16 4472  113
  103   32   15   16 5345   19  178   32    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0]

 

単純なモデルを作成する

モデルを定義するために Keras Sequential API を使用します。

  • 最初の層は Embedding 層です。この層は整数エンコードされた語彙を取り各単語インデックスのための埋め込みベクトルを検索します。これらのベクトルはモデルを訓練するときに学習されます。ベクトルは出力配列に次元を追加します。結果としての次元は : (batch, sequence, embedding) です。
  • 次に、GlobalAveragePooling1D 層はシークエンス次元に渡り平均することにより各サンプルのために固定長出力ベクトルを返します。これは可能な最も単純な方法の中で、モデルに可変長の入力を処理することを可能にします。
  • この固定長出力ベクトルが 16 隠れユニットを持つ完全結合 (Dense) 層を通してパイプされます。
  • 最後の層は単一出力ノードと密に結合されます。sigmoid 活性化関数を使用して、この値は 0 と 1 の間の float で、レビューがポジティブである確率 (or 確信度レベル) を表します。
embedding_dim=16

model = keras.Sequential([
  layers.Embedding(vocab_size, embedding_dim, input_length=maxlen),
  layers.GlobalAveragePooling1D(),
  layers.Dense(16, activation='relu'),
  layers.Dense(1, activation='sigmoid')
])

model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_1 (Embedding)      (None, 500, 16)           160000    
_________________________________________________________________
global_average_pooling1d (Gl (None, 16)                0         
_________________________________________________________________
dense (Dense)                (None, 16)                272       
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 17        
=================================================================
Total params: 160,289
Trainable params: 160,289
Non-trainable params: 0
_________________________________________________________________

 

モデルをコンパイルして訓練する

model.compile(optimizer='adam',
              loss='binary_crossentropy',
              metrics=['accuracy'])

history = model.fit(
    train_data,
    train_labels,
    epochs=30,
    batch_size=512,
    validation_split=0.2)
WARNING: Logging before flag parsing goes to stderr.
W0628 07:31:41.294968 139757629429504 deprecation.py:323] From /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/ops/math_grad.py:1250: add_dispatch_support..wrapper (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.where in 2.0, which has the same broadcast rule as np.where

Train on 20000 samples, validate on 5000 samples
Epoch 1/30
20000/20000 [==============================] - 2s 102us/sample - loss: 0.6920 - accuracy: 0.5317 - val_loss: 0.6905 - val_accuracy: 0.5176
Epoch 2/30
20000/20000 [==============================] - 2s 86us/sample - loss: 0.6869 - accuracy: 0.5848 - val_loss: 0.6824 - val_accuracy: 0.6300
Epoch 3/30
20000/20000 [==============================] - 2s 86us/sample - loss: 0.6754 - accuracy: 0.7176 - val_loss: 0.6660 - val_accuracy: 0.7378
Epoch 4/30
20000/20000 [==============================] - 2s 85us/sample - loss: 0.6533 - accuracy: 0.7517 - val_loss: 0.6376 - val_accuracy: 0.7610
Epoch 5/30
20000/20000 [==============================] - 2s 84us/sample - loss: 0.6190 - accuracy: 0.7843 - val_loss: 0.5988 - val_accuracy: 0.7814
Epoch 6/30
20000/20000 [==============================] - 2s 84us/sample - loss: 0.5749 - accuracy: 0.8049 - val_loss: 0.5537 - val_accuracy: 0.8020
Epoch 7/30
20000/20000 [==============================] - 2s 83us/sample - loss: 0.5272 - accuracy: 0.8214 - val_loss: 0.5095 - val_accuracy: 0.8198
Epoch 8/30
20000/20000 [==============================] - 2s 83us/sample - loss: 0.4793 - accuracy: 0.8444 - val_loss: 0.4687 - val_accuracy: 0.8296
Epoch 9/30
20000/20000 [==============================] - 2s 84us/sample - loss: 0.4363 - accuracy: 0.8586 - val_loss: 0.4327 - val_accuracy: 0.8490
Epoch 10/30
20000/20000 [==============================] - 2s 85us/sample - loss: 0.3987 - accuracy: 0.8714 - val_loss: 0.4043 - val_accuracy: 0.8590
Epoch 11/30
20000/20000 [==============================] - 2s 84us/sample - loss: 0.3676 - accuracy: 0.8788 - val_loss: 0.3809 - val_accuracy: 0.8656
Epoch 12/30
20000/20000 [==============================] - 2s 86us/sample - loss: 0.3421 - accuracy: 0.8853 - val_loss: 0.3635 - val_accuracy: 0.8672
Epoch 13/30
20000/20000 [==============================] - 2s 84us/sample - loss: 0.3203 - accuracy: 0.8917 - val_loss: 0.3485 - val_accuracy: 0.8714
Epoch 14/30
20000/20000 [==============================] - 2s 84us/sample - loss: 0.3017 - accuracy: 0.8978 - val_loss: 0.3367 - val_accuracy: 0.8736
Epoch 15/30
20000/20000 [==============================] - 2s 83us/sample - loss: 0.2871 - accuracy: 0.9031 - val_loss: 0.3274 - val_accuracy: 0.8756
Epoch 16/30
20000/20000 [==============================] - 2s 84us/sample - loss: 0.2728 - accuracy: 0.9059 - val_loss: 0.3251 - val_accuracy: 0.8720
Epoch 17/30
20000/20000 [==============================] - 2s 84us/sample - loss: 0.2606 - accuracy: 0.9106 - val_loss: 0.3132 - val_accuracy: 0.8814
Epoch 18/30
20000/20000 [==============================] - 2s 84us/sample - loss: 0.2488 - accuracy: 0.9146 - val_loss: 0.3067 - val_accuracy: 0.8830
Epoch 19/30
20000/20000 [==============================] - 2s 83us/sample - loss: 0.2387 - accuracy: 0.9183 - val_loss: 0.3021 - val_accuracy: 0.8838
Epoch 20/30
20000/20000 [==============================] - 2s 86us/sample - loss: 0.2293 - accuracy: 0.9205 - val_loss: 0.2986 - val_accuracy: 0.8844
Epoch 21/30
20000/20000 [==============================] - 2s 86us/sample - loss: 0.2213 - accuracy: 0.9238 - val_loss: 0.2946 - val_accuracy: 0.8872
Epoch 22/30
20000/20000 [==============================] - 2s 86us/sample - loss: 0.2150 - accuracy: 0.9260 - val_loss: 0.2920 - val_accuracy: 0.8880
Epoch 23/30
20000/20000 [==============================] - 2s 88us/sample - loss: 0.2068 - accuracy: 0.9289 - val_loss: 0.2906 - val_accuracy: 0.8876
Epoch 24/30
20000/20000 [==============================] - 2s 88us/sample - loss: 0.1996 - accuracy: 0.9305 - val_loss: 0.2874 - val_accuracy: 0.8904
Epoch 25/30
20000/20000 [==============================] - 2s 85us/sample - loss: 0.1928 - accuracy: 0.9327 - val_loss: 0.2867 - val_accuracy: 0.8888
Epoch 26/30
20000/20000 [==============================] - 2s 84us/sample - loss: 0.1875 - accuracy: 0.9348 - val_loss: 0.2858 - val_accuracy: 0.8894
Epoch 27/30
20000/20000 [==============================] - 2s 83us/sample - loss: 0.1818 - accuracy: 0.9384 - val_loss: 0.2850 - val_accuracy: 0.8896
Epoch 28/30
20000/20000 [==============================] - 2s 85us/sample - loss: 0.1759 - accuracy: 0.9398 - val_loss: 0.2829 - val_accuracy: 0.8908
Epoch 29/30
20000/20000 [==============================] - 2s 84us/sample - loss: 0.1703 - accuracy: 0.9430 - val_loss: 0.2825 - val_accuracy: 0.8918
Epoch 30/30
20000/20000 [==============================] - 2s 83us/sample - loss: 0.1654 - accuracy: 0.9440 - val_loss: 0.2846 - val_accuracy: 0.8902

このアプローチで私達のモデルは 88% まわりの検証精度に到達します (モデルは overfitting していることに注意してください、訓練精度はかなり高いです)。

import matplotlib.pyplot as plt

history_dict = history.history

acc = history_dict['accuracy']
val_acc = history_dict['val_accuracy']
loss = history_dict['loss']
val_loss = history_dict['val_loss']

epochs = range(1, len(acc) + 1)

plt.figure(figsize=(12,9))
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()

plt.figure(figsize=(12,9))
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')
plt.ylim((0.5,1))
plt.show()
<Figure size 1200x900 with 1 Axes>
<Figure size 1200x900 with 1 Axes>

 

学習された埋め込みを取得する

次に、訓練の間に学習された単語埋め込みを取得しましょう。これは shape (vocab_size,embedding-dimension) の行列です。

e = model.layers[0]
weights = e.get_weights()[0]
print(weights.shape) # shape: (vocab_size, embedding_dim)
(10000, 16)

さて重みをディスクに書きます。Embedding Projector を使用するには、タブ区切りフォーマットで 2 つのファイルをアップロードします : (埋め込みを含む) ベクトルのファイル、そして (単語を含む) メタデータのファイルです。

import io

out_v = io.open('vecs.tsv', 'w', encoding='utf-8')
out_m = io.open('meta.tsv', 'w', encoding='utf-8')
for word_num in range(vocab_size):
  word = reverse_word_index[word_num]
  embeddings = weights[word_num]
  out_m.write(word + "\n")
  out_v.write('\t'.join([str(x) for x in embeddings]) + "\n")
out_v.close()
out_m.close()

このチュートリアルを Colaboratory で実行している場合、これらのファイルを貴方のローカルマシンにダウンロードするために次のスニペットを使用できます (or ファイルブラウザを使用してください、View -> Table of contents -> File browser)。

try:
  from google.colab import files
except ImportError:
  pass
else:
  files.download('vecs.tsv')
  files.download('meta.tsv')

 

埋め込みを可視化する

私達の埋め込みを可視化するには、それらを埋め込みプロジェクターにアップロードします。

Embedding Projector をオープンします。

  • “Load data” をクリックする
  • 上で作成された 2 つのファイルをアップロードする : vecs.tsv と meta.tsv.

貴方が訓練した埋め込みが今表示されます。単語をそれらに最も近い近傍を見つけるために検索することができます。例えば、”beautiful” を検索してみてください。”wonderful” のような近傍を見るかもしれません。Note: 埋め込み層を訓練する前に重みがどのようにランダムに初期化されたかに依拠して、貴方の結果は少し異なるかもしれません。

Note: 実験的に、より単純なモデルを使用してより解釈可能な埋め込みを生成できるかもしれません。Dense(16) 層を削除して、モデルを再訓練し、そして埋め込みを再度可視化してみてください。

 

Next steps

このチュートリアルは小さいデータセット上でスクラッチからどのように単語埋め込みを訓練して可視化するかを示しました。

  • Kreas で埋め込みについて更に学習するためには、François Chollet のこれらの ノートブック を勧めます。
  • テキスト分類について更に学習するためには (全体的なワークフローを含み、そしてもし貴方がいつ埋め込み vs one-hot エンコーディングを使用するかについて関心があれば)、この実践的なテキスト分類 ガイド を勧めます。
 

以上



TensorFlow 2.0 Beta : Tutorials : 画像 :- Keras で TensorFlow Hub

TensorFlow 2.0 Beta : Beginner Tutorials : 画像 :- Keras で TensorFlow Hub (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 06/30/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Beta – Beginner Tutorials – Images の以下のページを翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

 

画像 :- Keras で TensorFlow Hub

TensorFlow Hub は事前訓練されたモデル・コンポーネントを共有するための方法です。事前訓練モデルの検索可能なリストのためには TensorFlow Module Hubを見てください。このチュートリアルは以下を実演します :

  1. tf.keras で TensorFlow Hub をどのように使用するか。
  2. TensorFlow Hub を使用してどのように画像分類を行なうか。
  3. どのように単純な転移学習を行なうか。

 

セットアップ

from __future__ import absolute_import, division, print_function, unicode_literals

import matplotlib.pylab as plt

!pip install -q tensorflow-gpu==2.0.0-beta1
import tensorflow as tf
!pip install -q tensorflow_hub
import tensorflow_hub as hub

from tensorflow.keras import layers

 

ImageNet 分類器

分類器をダウンロードする

mobilenet をロードするために hub.module を、それを keras 層としてラップするために tf.keras.layers.Lambda を使用します。tfhub.dev からのどのような TensorFlow 2 互換画像分類器 URL もここで動作します。

classifier_url ="https://tfhub.dev/google/tf2-preview/mobilenet_v2/classification/2" #@param {type:"string"}
IMAGE_SHAPE = (224, 224)

classifier = tf.keras.Sequential([
    hub.KerasLayer(classifier_url, input_shape=IMAGE_SHAPE+(3,))
])

 

単一画像上でそれを実行する

モデルを試すために単一のイメージをダウンロードします。

import numpy as np
import PIL.Image as Image

grace_hopper = tf.keras.utils.get_file('image.jpg','https://storage.googleapis.com/download.tensorflow.org/example_images/grace_hopper.jpg')
grace_hopper = Image.open(grace_hopper).resize(IMAGE_SHAPE)
grace_hopper
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/grace_hopper.jpg
65536/61306 [================================] - 0s 0us/step

grace_hopper = np.array(grace_hopper)/255.0
grace_hopper.shape
(224, 224, 3)

バッチ次元を追加して、画像をモデルに渡します。

result = classifier.predict(grace_hopper[np.newaxis, ...])
result.shape
(1, 1001)

結果はロジットの 1001 要素ベクトルで、画像のための各クラスの確率を見積もります。

そして top クラス ID は argmax で見つけられます :

predicted_class = np.argmax(result[0], axis=-1)
predicted_class
653

 

予測をデコードする

予測されたクラス ID を持ち、ImageNet ラベルを取得して、そして予測をデコードします。

labels_path = tf.keras.utils.get_file('ImageNetLabels.txt','https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt')
imagenet_labels = np.array(open(labels_path).read().splitlines())
plt.imshow(grace_hopper)
plt.axis('off')
predicted_class_name = imagenet_labels[predicted_class]
_ = plt.title("Prediction: " + predicted_class_name.title())

 

単純な転移学習

TF Hub を使用するとき私達のデータセットでクラスを認識するためにモデルのトップ層を再訓練することは単純です。

 

データセット

このサンプルのために TensorFlow flowers データセットを使用します :

data_root = tf.keras.utils.get_file(
  'flower_photos','https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz',
   untar=True)
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz
228818944/228813984 [==============================] - 4s 0us/step

このデータをモデルにロードする最も単純な方法は tf.keras.preprocessing.image.ImageDataGenerator を使用することです。

TensorFlow Hub の画像モジュールの総ては [0, 1] 範囲のfloat 入力を想定しています。これを獲得するために ImageDataGenerator の rescale パラメータを使用します。

画像サイズは後で処理されます。

image_generator = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1/255)
image_data = image_generator.flow_from_directory(str(data_root), target_size=IMAGE_SHAPE)
Found 3670 images belonging to 5 classes.

結果としてのオブジェクトは image_batch, label_batch ペアを返す iterator です。

for image_batch, label_batch in image_data:
  print("Image batch shape: ", image_batch.shape)
  print("Label batch shape: ", label_batch.shape)
  break
Image batch shape:  (32, 224, 224, 3)
Label batch shape:  (32, 5)

 

画像のバッチ上で分類器を実行する

さて画像バッチ上で分類器を実行します。

result_batch = classifier.predict(image_batch)
result_batch.shape
(32, 1001)
predicted_class_names = imagenet_labels[np.argmax(result_batch, axis=-1)]
predicted_class_names
array(['daisy', 'coral fungus', 'dining table', 'feather boa',
       'park bench', 'daisy', 'bakery', 'daisy', 'feather boa', 'pot',
       'ice cream', 'picket fence', 'daisy', 'vase', 'confectionery',
       'picket fence', 'daisy', 'daisy', 'picket fence', 'sunglasses',
       'porcupine', 'picket fence', "yellow lady's slipper", 'daisy',
       'mushroom', 'Lhasa', 'picket fence', 'daisy', 'daisy', 'rapeseed',
       'bee', 'picket fence'], dtype='<U30')

今はこれらの予測が画像とともにどのように並ぶかを確認します :

plt.figure(figsize=(10,9))
plt.subplots_adjust(hspace=0.5)
for n in range(30):
  plt.subplot(6,5,n+1)
  plt.imshow(image_batch[n])
  plt.title(predicted_class_names[n])
  plt.axis('off')
_ = plt.suptitle("ImageNet predictions")

画像属性については LICENSE.txt 参照。

結果はパーフェクトからは程遠いですが、これらはモデルがそのために訓練されたクラスではないというのが合理的な考えです (“daisy” を除いて)。

 

ヘッドレス・モデルをダウンロードする

TensorFlow Hub はまた top 分類層なしのモデルも配布しています。これらは容易に転移学習を行なうために使用できます。

tfhub.dev からのどのようなどのような TensorFlow 2 互換画像分類器 URL もここで動作します。

feature_extractor_url = "https://tfhub.dev/google/tf2-preview/mobilenet_v2/feature_vector/2" #@param {type:"string"}

特徴抽出器を作成します。

feature_extractor_layer = hub.KerasLayer(feature_extractor_url,
                                         input_shape=(224,224,3))

それは各画像の 1280-長ベクトルを返します :

feature_batch = feature_extractor_layer(image_batch)
print(feature_batch.shape)
(32, 1280)

特徴抽出器層の変数を凍結します、その結果訓練は新しい分類層だけを変更します。

feature_extractor_layer.trainable = False

 

分類ヘッドを装着する

さて hub 層を tf.keras.Sequential モデルでラップし、新しい分類層を追加します。

model = tf.keras.Sequential([
  feature_extractor_layer,
  layers.Dense(image_data.num_classes, activation='softmax')
])

model.summary()
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
keras_layer_1 (KerasLayer)   (None, 1280)              2257984   
_________________________________________________________________
dense (Dense)                (None, 5)                 6405      
=================================================================
Total params: 2,264,389
Trainable params: 6,405
Non-trainable params: 2,257,984
_________________________________________________________________
predictions = model(image_batch)
predictions.shape
TensorShape([32, 5])

 

モデルを訓練する

訓練プロセスを configure するために compile を使用します :

model.compile(
  optimizer=tf.keras.optimizers.Adam(),
  loss='categorical_crossentropy',
  metrics=['acc'])

そしてモデルを訓練するために .fit メソッドを使用します。

このサンプルを短く保持するために 2 エポックだけ訓練します。訓練進捗を可視化するために、エポック平均の代わりに各バッチの個々の損失と精度を記録するためにカスタム callback を使用します。

class CollectBatchStats(tf.keras.callbacks.Callback):
  def __init__(self):
    self.batch_losses = []
    self.batch_acc = []

  def on_train_batch_end(self, batch, logs=None):
    self.batch_losses.append(logs['loss'])
    self.batch_acc.append(logs['acc'])
    self.model.reset_metrics()
steps_per_epoch = np.ceil(image_data.samples/image_data.batch_size)

batch_stats_callback = CollectBatchStats()

history = model.fit(image_data, epochs=2,
                    steps_per_epoch=steps_per_epoch,
                    callbacks = [batch_stats_callback])
Epoch 1/2

WARNING: Logging before flag parsing goes to stderr.
W0628 03:59:21.830983 139712134772480 deprecation.py:323] From /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/ops/math_grad.py:1250: add_dispatch_support..wrapper (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.where in 2.0, which has the same broadcast rule as np.where

115/115 [==============================] - 23s 196ms/step - loss: 0.7078 - acc: 0.8438
Epoch 2/2
115/115 [==============================] - 22s 194ms/step - loss: 0.3480 - acc: 0.7812

さて 2, 3 の訓練反復後でさえ、モデルがタスクにおいて進捗していることを既に見ることができます。

plt.figure()
plt.ylabel("Loss")
plt.xlabel("Training Steps")
plt.ylim([0,2])
plt.plot(batch_stats_callback.batch_losses)
[<matplotlib.lines.Line2D at 0x7f0be83547f0>]

plt.figure()
plt.ylabel("Accuracy")
plt.xlabel("Training Steps")
plt.ylim([0,1])
plt.plot(batch_stats_callback.batch_acc)
[<matplotlib.lines.Line2D at 0x7f0be17fe128>]

 

予測をチェックする

前からのプロットを再び行なうために、最初にクラス名の順序付けられたリストを得ます :

class_names = sorted(image_data.class_indices.items(), key=lambda pair:pair[1])
class_names = np.array([key.title() for key, value in class_names])
class_names
array(['Daisy', 'Dandelion', 'Roses', 'Sunflowers', 'Tulips'],
      dtype='<U10')

モデルを通して画像バッチを実行してインデックスをクラス名に変換します。

predicted_batch = model.predict(image_batch)
predicted_id = np.argmax(predicted_batch, axis=-1)
predicted_label_batch = class_names[predicted_id]

結果をプロットします。

label_id = np.argmax(label_batch, axis=-1)
plt.figure(figsize=(10,9))
plt.subplots_adjust(hspace=0.5)
for n in range(30):
  plt.subplot(6,5,n+1)
  plt.imshow(image_batch[n])
  color = "green" if predicted_id[n] == label_id[n] else "red"
  plt.title(predicted_label_batch[n].title(), color=color)
  plt.axis('off')
_ = plt.suptitle("Model predictions (green: correct, red: incorrect)")

 

貴方のモデルをエクスポートする

モデルを訓練した今、それを saved model としてエクスポートします :

import time
t = time.time()

export_path = "/tmp/saved_models/{}".format(int(t))
tf.keras.experimental.export_saved_model(model, export_path)

export_path
W0628 04:00:17.005649 139712134772480 deprecation.py:323] From /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/saved_model/signature_def_utils_impl.py:253: build_tensor_info (from tensorflow.python.saved_model.utils_impl) is deprecated and will be removed in a future version.
Instructions for updating:
This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.utils.build_tensor_info or tf.compat.v1.saved_model.build_tensor_info.
W0628 04:00:17.007575 139712134772480 export_utils.py:182] Export includes no default signature!
W0628 04:00:17.746208 139712134772480 meta_graph.py:450] Issue encountered when serializing variables.
Type is unsupported, or the types of the items don't match field type in CollectionDef. Note this is a warning and probably safe to ignore.
'list' object has no attribute 'name'
W0628 04:00:24.953796 139712134772480 export_utils.py:182] Export includes no default signature!
W0628 04:00:25.634641 139712134772480 meta_graph.py:450] Issue encountered when serializing variables.
Type is unsupported, or the types of the items don't match field type in CollectionDef. Note this is a warning and probably safe to ignore.
'list' object has no attribute 'name'
Exception ignored in: <bound method _CheckpointRestoreCoordinator.__del__ of <tensorflow.python.training.tracking.util._CheckpointRestoreCoordinator object at 0x7f0b50286eb8>>
Traceback (most recent call last):
  File "/tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/training/tracking/util.py", line 244, in __del__
    .format(pretty_printer.node_names[node_id]))
  File "/tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/training/tracking/util.py", line 93, in node_names
    path_to_root[node_id] + (child.local_name,))
  File "/tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/training/tracking/object_identity.py", line 76, in __getitem__
    return self._storage[self._wrap_key(key)]
KeyError: (<tensorflow.python.training.tracking.object_identity._ObjectIdentityWrapper object at 0x7f0b43e18e48>,)
W0628 04:00:34.214757 139712134772480 meta_graph.py:450] Issue encountered when serializing variables.
Type is unsupported, or the types of the items don't match field type in CollectionDef. Note this is a warning and probably safe to ignore.
'list' object has no attribute 'name'

'/tmp/saved_models/1561694408'

今はそれを再ロードできて、それが依然として同じ結果を与えることを確認しましょう :

reloaded = tf.keras.experimental.load_from_saved_model(export_path, custom_objects={'KerasLayer':hub.KerasLayer})
Exception ignored in: <bound method _CheckpointRestoreCoordinator.__del__ of <tensorflow.python.training.tracking.util._CheckpointRestoreCoordinator object at 0x7f0b40ed2a20>>
Traceback (most recent call last):
  File "/tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/training/tracking/util.py", line 244, in __del__
    .format(pretty_printer.node_names[node_id]))
  File "/tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/training/tracking/util.py", line 93, in node_names
    path_to_root[node_id] + (child.local_name,))
  File "/tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/training/tracking/object_identity.py", line 76, in __getitem__
    return self._storage[self._wrap_key(key)]
KeyError: (<tensorflow.python.training.tracking.object_identity._ObjectIdentityWrapper object at 0x7f0b3b9485f8>,)
result_batch = model.predict(image_batch)
reloaded_result_batch = reloaded.predict(image_batch)
abs(reloaded_result_batch - result_batch).max()

0.0

savd model は後で推論のためにロードするか、TFLiteTFjs のために変換できます。

 

以上



TensorFlow 2.0 Beta : Tutorials : 画像 :- 事前訓練された ConvNet を使用する転移学習

TensorFlow 2.0 Beta : Beginner Tutorials : 画像 :- 事前訓練された ConvNet を使用する転移学習 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 06/27/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Beta – Beginner Tutorials – Images の以下のページを翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

画像 :- 事前訓練された ConvNet を使用する転移学習

このチュートリアルでは事前訓練されたネットワークからの転移学習を使用して猫 vs 犬画像をどのように分類するかを議論します。これはスクラッチからネットワークを訓練することによって見たものより高い精度を得ることを可能にします。

事前訓練されたモデル は、典型的には巨大スケール画像分類タスク上の、巨大なデータセット上で以前に訓練された (セーブされた) ネットワークです。事前訓練されたモデルをそのまま使用するか与えられたタスクにこのモデルをカスタマイズするために 転移学習 を利用できます。

転移学習の背後にある直感は、このモデルが十分に巨大で一般的なデータセット上で訓練された場合、このモデルは視覚世界の一般的なモデルとして効果的に役立つであろうということです。それから巨大なデータセット上で巨大なモデルをスクラッチから訓練し始めることなくこれらの学習された特徴マップを活用できます。

このノートブックでは、事前訓練されたモデルをカスタマイズするために 2 つの方法を試すでしょう :

  1. 特徴抽出 (= Feature Extraction) – 新しいサンプルから意味がある特徴を抽出するために以前のネットワークにより学習された表現を使用します。事前訓練されたモデルの上に (スクラッチから訓練される) 新しい分類器を単に追加します、その結果前に学習された特徴マップを私達のデータセットのために再目的化できます。

    モデル全体を (再) 訓練する必要はありません。ベース畳み込みネットワークは既に写真を分類するために一般的に有用な特徴を既に含んでいます。けれども、事前訓練されたモデルの最後の分類パートはしばしば元の分類タスクに特有で、結果的にモデルが訓練された上のクラスのセットに固有です。

  2. 再調整 (= Fine-Tuning) – 特徴抽出のために使用される凍結されたモデルベースの 2, 3 のトップ層を解凍して、新たに追加された分類層とベースモデルの最後の層群の両者を一緒に訓練します。これはベースモデルの高次特徴表現を、特定のタスクのためにより関連付けるために「再調整」することを可能にします。

一般的な機械学習ワークフローに従います。

  1. データを調べて理解する。
  2. 入力パイプラインを構築します、このケースでは Keras ImageDataGenerator を使用します。
  3. モデルを構成する。
  4. 事前訓練されたベースモデル (と事前訓練された重み) をロードする
  5. top に分類層をスタックする。
  6. モデルを訓練する。
  7. モデルを評価する。
from __future__ import absolute_import, division, print_function, unicode_literals

import os

import numpy as np

import matplotlib.pyplot as plt
!pip install -q tensorflow-gpu==2.0.0-beta1
import tensorflow as tf

keras = tf.keras

 

データ前処理

データ・ダウンロード

猫と犬のデータセットをロードするために TensorFlow Dataset を利用します。

この tfds パッケージは事前定義されたデータをロードするための最も容易な方法です。もし貴方自身のデータを持ち、インポートしてそれを TensorFlow で使用することに興味があれば loading image data を見てください。

import tensorflow_datasets as tfds
tfds.disable_progress_bar()

tfds.load メソッドはデータをダウンロードしてキャッシュし、tf.data.Dataset オブジェクトを返します。これらのオブジェクトはデータを操作してそれをモデルにパイプするためのパワフルで、効率的なメソッドを提供します。

“cats_vs_dog” は標準的な分割を定義していないので、それをデータの 80%, 10%, 10% で (train, validation, test) にそれぞれ分割するために subsplit 機能を使用します。

SPLIT_WEIGHTS = (8, 1, 1)
splits = tfds.Split.TRAIN.subsplit(weighted=SPLIT_WEIGHTS)

(raw_train, raw_validation, raw_test), metadata = tfds.load(
    'cats_vs_dogs', split=list(splits),
    with_info=True, as_supervised=True)
Downloading and preparing dataset cats_vs_dogs (786.68 MiB) to /home/kbuilder/tensorflow_datasets/cats_vs_dogs/2.0.1...

WARNING: Logging before flag parsing goes to stderr.
W0614 17:11:52.072730 140109792941824 cats_vs_dogs.py:107] 1738 images were corrupted and were skipped
W0614 17:11:52.085152 140109792941824 deprecation.py:323] From /home/kbuilder/.local/lib/python3.5/site-packages/tensorflow_datasets/core/file_format_adapter.py:209: tf_record_iterator (from tensorflow.python.lib.io.tf_record) is deprecated and will be removed in a future version.
Instructions for updating:
Use eager execution and: 
`tf.data.TFRecordDataset(path)`

Dataset cats_vs_dogs downloaded and prepared to /home/kbuilder/tensorflow_datasets/cats_vs_dogs/2.0.1. Subsequent calls will reuse this data.

結果としての tf.data.Dataset オブジェクトは (image, label) ペアを含みます。そこでは画像は可変な shape と 3 チャネルを持ち、ラベルはスカラーです。

print(raw_train)
print(raw_validation)
print(raw_test)
<_OptionsDataset shapes: ((None, None, 3), ()), types: (tf.uint8, tf.int64)>
<_OptionsDataset shapes: ((None, None, 3), ()), types: (tf.uint8, tf.int64)>
<_OptionsDataset shapes: ((None, None, 3), ()), types: (tf.uint8, tf.int64)>

訓練セットから最初の 2 つの画像とラベルを表示します。

get_label_name = metadata.features['label'].int2str

for image, label in raw_train.take(2):
  plt.figure()
  plt.imshow(image)
  plt.title(get_label_name(label))

 

データをフォーマットする

タスクのために画像をフォーマットするために tf.image モジュールを使用します。

画像を固定入力サイズにリサイズして、入力チャネルを [-1, 1] の範囲にリスケールします。

IMG_SIZE = 160 # All images will be resized to 160x160

def format_example(image, label):
  image = tf.cast(image, tf.float32)
  image = (image/127.5) - 1
  image = tf.image.resize(image, (IMG_SIZE, IMG_SIZE))
  return image, label

map メソッドを使用してこの関数をデータセットの各アイテムに適用します :

train = raw_train.map(format_example)
validation = raw_validation.map(format_example)
test = raw_test.map(format_example)

データをシャッフルしてバッチ化します。

BATCH_SIZE = 32
SHUFFLE_BUFFER_SIZE = 1000
train_batches = train.shuffle(SHUFFLE_BUFFER_SIZE).batch(BATCH_SIZE)
validation_batches = validation.batch(BATCH_SIZE)
test_batches = test.batch(BATCH_SIZE)

データのバッチを調べます :

for image_batch, label_batch in train_batches.take(1):
  pass

image_batch.shape
TensorShape([32, 160, 160, 3])

 

事前訓練された convnet からベースモデルを作成する

Google で開発された MobileNet V2 モデルからベースモデルを作成します。これは ImageNet データセット、web 画像の 1.4 M 画像と 1000 クラスの巨大データセット上で事前訓練されています。ImageNet はパンノキと注射器のようなカテゴリを持つ非常に恣意的な研究訓練データセットを持ちますが、この知識の土台は特定のデータセットから猫と犬を識別するのに役立ちます。

最初に、特徴抽出のために使用する MobileNet V2 の層を選択する必要があります。明らかに、最も最後の分類層 (「トップ」上、何故ならば機械学習モデルの殆どの図はボトムからトップに進みます) は全く役立ちません。代わりに、flatten 演算の前の最も最後の層に依拠する一般的な実践に従います。この層は「ボトルネック層」と呼ばれます。ボトルネック特徴は final/top 層に比較して遥かに汎用性を保持します。

まずは、ImageNet 上で訓練された重みとともに事前ロードされた MobileNet V2 モデルをインスタンス化します。include_top=False 引数を指定することにより、トップに分類層を含まないネットワークをロードします、これは特徴抽出に理想的です。

IMG_SHAPE = (IMG_SIZE, IMG_SIZE, 3)

# Create the base model from the pre-trained model MobileNet V2
base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE,
                                               include_top=False,
                                               weights='imagenet')
Downloading data from https://github.com/JonathanCMitchell/mobilenet_v2_keras/releases/download/v1.1/mobilenet_v2_weights_tf_dim_ordering_tf_kernels_1.0_160_no_top.h5
9412608/9406464 [==============================] - 2s 0us/step

この特徴抽出器は各 160x160x3 画像を 5x5x1280 特徴ブロックに変換します。画像のサンプルバッチにそれが何をするかを見ます :

feature_batch = base_model(image_batch)
print(feature_batch.shape)
(32, 5, 5, 1280)

 

特徴抽出

前のステップで作成された畳み込みベースを凍結してそれを特徴抽出器として使用し、その上に分類器を追加して top-level 分類器を訓練します。

 

畳み込みベースを凍結する

compile してモデルを訓練する前に畳み込みベースを凍結することは重要です。凍結する (あるいは layer.trainable = False を設定する) ことにより、与えられた層の重みが訓練の間に更新されることを回避します。MobileNet V2 は多くの層を持ちますが、全体のモデルの trainable フラグを False に設定すれば総ての層を凍結します。

base_model.trainable = False
# Let's take a look at the base model architecture
base_model.summary()
Model: "mobilenetv2_1.00_160"
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
input_1 (InputLayer)            [(None, 160, 160, 3) 0                                            
__________________________________________________________________________________________________
Conv1_pad (ZeroPadding2D)       (None, 161, 161, 3)  0           input_1[0][0]                    
__________________________________________________________________________________________________
Conv1 (Conv2D)                  (None, 80, 80, 32)   864         Conv1_pad[0][0]                  
__________________________________________________________________________________________________
bn_Conv1 (BatchNormalization)   (None, 80, 80, 32)   128         Conv1[0][0]                      
__________________________________________________________________________________________________
Conv1_relu (ReLU)               (None, 80, 80, 32)   0           bn_Conv1[0][0]                   
__________________________________________________________________________________________________
expanded_conv_depthwise (Depthw (None, 80, 80, 32)   288         Conv1_relu[0][0]                 
__________________________________________________________________________________________________
expanded_conv_depthwise_BN (Bat (None, 80, 80, 32)   128         expanded_conv_depthwise[0][0]    
__________________________________________________________________________________________________
expanded_conv_depthwise_relu (R (None, 80, 80, 32)   0           expanded_conv_depthwise_BN[0][0] 
__________________________________________________________________________________________________
expanded_conv_project (Conv2D)  (None, 80, 80, 16)   512         expanded_conv_depthwise_relu[0][0
__________________________________________________________________________________________________
expanded_conv_project_BN (Batch (None, 80, 80, 16)   64          expanded_conv_project[0][0]      
__________________________________________________________________________________________________
block_1_expand (Conv2D)         (None, 80, 80, 96)   1536        expanded_conv_project_BN[0][0]   
__________________________________________________________________________________________________
block_1_expand_BN (BatchNormali (None, 80, 80, 96)   384         block_1_expand[0][0]             
__________________________________________________________________________________________________
block_1_expand_relu (ReLU)      (None, 80, 80, 96)   0           block_1_expand_BN[0][0]          
__________________________________________________________________________________________________
block_1_pad (ZeroPadding2D)     (None, 81, 81, 96)   0           block_1_expand_relu[0][0]        
__________________________________________________________________________________________________
block_1_depthwise (DepthwiseCon (None, 40, 40, 96)   864         block_1_pad[0][0]                
__________________________________________________________________________________________________
block_1_depthwise_BN (BatchNorm (None, 40, 40, 96)   384         block_1_depthwise[0][0]          
__________________________________________________________________________________________________
block_1_depthwise_relu (ReLU)   (None, 40, 40, 96)   0           block_1_depthwise_BN[0][0]       
__________________________________________________________________________________________________
block_1_project (Conv2D)        (None, 40, 40, 24)   2304        block_1_depthwise_relu[0][0]     
__________________________________________________________________________________________________
block_1_project_BN (BatchNormal (None, 40, 40, 24)   96          block_1_project[0][0]            
__________________________________________________________________________________________________
block_2_expand (Conv2D)         (None, 40, 40, 144)  3456        block_1_project_BN[0][0]         
__________________________________________________________________________________________________
block_2_expand_BN (BatchNormali (None, 40, 40, 144)  576         block_2_expand[0][0]             
__________________________________________________________________________________________________
block_2_expand_relu (ReLU)      (None, 40, 40, 144)  0           block_2_expand_BN[0][0]          
__________________________________________________________________________________________________
block_2_depthwise (DepthwiseCon (None, 40, 40, 144)  1296        block_2_expand_relu[0][0]        
__________________________________________________________________________________________________
block_2_depthwise_BN (BatchNorm (None, 40, 40, 144)  576         block_2_depthwise[0][0]          
__________________________________________________________________________________________________
block_2_depthwise_relu (ReLU)   (None, 40, 40, 144)  0           block_2_depthwise_BN[0][0]       
__________________________________________________________________________________________________
block_2_project (Conv2D)        (None, 40, 40, 24)   3456        block_2_depthwise_relu[0][0]     
__________________________________________________________________________________________________
block_2_project_BN (BatchNormal (None, 40, 40, 24)   96          block_2_project[0][0]            
__________________________________________________________________________________________________
block_2_add (Add)               (None, 40, 40, 24)   0           block_1_project_BN[0][0]         
                                                                 block_2_project_BN[0][0]         
__________________________________________________________________________________________________
block_3_expand (Conv2D)         (None, 40, 40, 144)  3456        block_2_add[0][0]                
__________________________________________________________________________________________________
block_3_expand_BN (BatchNormali (None, 40, 40, 144)  576         block_3_expand[0][0]             
__________________________________________________________________________________________________
block_3_expand_relu (ReLU)      (None, 40, 40, 144)  0           block_3_expand_BN[0][0]          
__________________________________________________________________________________________________
block_3_pad (ZeroPadding2D)     (None, 41, 41, 144)  0           block_3_expand_relu[0][0]        
__________________________________________________________________________________________________
block_3_depthwise (DepthwiseCon (None, 20, 20, 144)  1296        block_3_pad[0][0]                
__________________________________________________________________________________________________
block_3_depthwise_BN (BatchNorm (None, 20, 20, 144)  576         block_3_depthwise[0][0]          
__________________________________________________________________________________________________
block_3_depthwise_relu (ReLU)   (None, 20, 20, 144)  0           block_3_depthwise_BN[0][0]       
__________________________________________________________________________________________________
block_3_project (Conv2D)        (None, 20, 20, 32)   4608        block_3_depthwise_relu[0][0]     
__________________________________________________________________________________________________
block_3_project_BN (BatchNormal (None, 20, 20, 32)   128         block_3_project[0][0]            
__________________________________________________________________________________________________
block_4_expand (Conv2D)         (None, 20, 20, 192)  6144        block_3_project_BN[0][0]         
__________________________________________________________________________________________________
block_4_expand_BN (BatchNormali (None, 20, 20, 192)  768         block_4_expand[0][0]             
__________________________________________________________________________________________________
block_4_expand_relu (ReLU)      (None, 20, 20, 192)  0           block_4_expand_BN[0][0]          
__________________________________________________________________________________________________
block_4_depthwise (DepthwiseCon (None, 20, 20, 192)  1728        block_4_expand_relu[0][0]        
__________________________________________________________________________________________________
block_4_depthwise_BN (BatchNorm (None, 20, 20, 192)  768         block_4_depthwise[0][0]          
__________________________________________________________________________________________________
block_4_depthwise_relu (ReLU)   (None, 20, 20, 192)  0           block_4_depthwise_BN[0][0]       
__________________________________________________________________________________________________
block_4_project (Conv2D)        (None, 20, 20, 32)   6144        block_4_depthwise_relu[0][0]     
__________________________________________________________________________________________________
block_4_project_BN (BatchNormal (None, 20, 20, 32)   128         block_4_project[0][0]            
__________________________________________________________________________________________________
block_4_add (Add)               (None, 20, 20, 32)   0           block_3_project_BN[0][0]         
                                                                 block_4_project_BN[0][0]         
__________________________________________________________________________________________________
block_5_expand (Conv2D)         (None, 20, 20, 192)  6144        block_4_add[0][0]                
__________________________________________________________________________________________________
block_5_expand_BN (BatchNormali (None, 20, 20, 192)  768         block_5_expand[0][0]             
__________________________________________________________________________________________________
block_5_expand_relu (ReLU)      (None, 20, 20, 192)  0           block_5_expand_BN[0][0]          
__________________________________________________________________________________________________
block_5_depthwise (DepthwiseCon (None, 20, 20, 192)  1728        block_5_expand_relu[0][0]        
__________________________________________________________________________________________________
block_5_depthwise_BN (BatchNorm (None, 20, 20, 192)  768         block_5_depthwise[0][0]          
__________________________________________________________________________________________________
block_5_depthwise_relu (ReLU)   (None, 20, 20, 192)  0           block_5_depthwise_BN[0][0]       
__________________________________________________________________________________________________
block_5_project (Conv2D)        (None, 20, 20, 32)   6144        block_5_depthwise_relu[0][0]     
__________________________________________________________________________________________________
block_5_project_BN (BatchNormal (None, 20, 20, 32)   128         block_5_project[0][0]            
__________________________________________________________________________________________________
block_5_add (Add)               (None, 20, 20, 32)   0           block_4_add[0][0]                
                                                                 block_5_project_BN[0][0]         
__________________________________________________________________________________________________
block_6_expand (Conv2D)         (None, 20, 20, 192)  6144        block_5_add[0][0]                
__________________________________________________________________________________________________
block_6_expand_BN (BatchNormali (None, 20, 20, 192)  768         block_6_expand[0][0]             
__________________________________________________________________________________________________
block_6_expand_relu (ReLU)      (None, 20, 20, 192)  0           block_6_expand_BN[0][0]          
__________________________________________________________________________________________________
block_6_pad (ZeroPadding2D)     (None, 21, 21, 192)  0           block_6_expand_relu[0][0]        
__________________________________________________________________________________________________
block_6_depthwise (DepthwiseCon (None, 10, 10, 192)  1728        block_6_pad[0][0]                
__________________________________________________________________________________________________
block_6_depthwise_BN (BatchNorm (None, 10, 10, 192)  768         block_6_depthwise[0][0]          
__________________________________________________________________________________________________
block_6_depthwise_relu (ReLU)   (None, 10, 10, 192)  0           block_6_depthwise_BN[0][0]       
__________________________________________________________________________________________________
block_6_project (Conv2D)        (None, 10, 10, 64)   12288       block_6_depthwise_relu[0][0]     
__________________________________________________________________________________________________
block_6_project_BN (BatchNormal (None, 10, 10, 64)   256         block_6_project[0][0]            
__________________________________________________________________________________________________
block_7_expand (Conv2D)         (None, 10, 10, 384)  24576       block_6_project_BN[0][0]         
__________________________________________________________________________________________________
block_7_expand_BN (BatchNormali (None, 10, 10, 384)  1536        block_7_expand[0][0]             
__________________________________________________________________________________________________
block_7_expand_relu (ReLU)      (None, 10, 10, 384)  0           block_7_expand_BN[0][0]          
__________________________________________________________________________________________________
block_7_depthwise (DepthwiseCon (None, 10, 10, 384)  3456        block_7_expand_relu[0][0]        
__________________________________________________________________________________________________
block_7_depthwise_BN (BatchNorm (None, 10, 10, 384)  1536        block_7_depthwise[0][0]          
__________________________________________________________________________________________________
block_7_depthwise_relu (ReLU)   (None, 10, 10, 384)  0           block_7_depthwise_BN[0][0]       
__________________________________________________________________________________________________
block_7_project (Conv2D)        (None, 10, 10, 64)   24576       block_7_depthwise_relu[0][0]     
__________________________________________________________________________________________________
block_7_project_BN (BatchNormal (None, 10, 10, 64)   256         block_7_project[0][0]            
__________________________________________________________________________________________________
block_7_add (Add)               (None, 10, 10, 64)   0           block_6_project_BN[0][0]         
                                                                 block_7_project_BN[0][0]         
__________________________________________________________________________________________________
block_8_expand (Conv2D)         (None, 10, 10, 384)  24576       block_7_add[0][0]                
__________________________________________________________________________________________________
block_8_expand_BN (BatchNormali (None, 10, 10, 384)  1536        block_8_expand[0][0]             
__________________________________________________________________________________________________
block_8_expand_relu (ReLU)      (None, 10, 10, 384)  0           block_8_expand_BN[0][0]          
__________________________________________________________________________________________________
block_8_depthwise (DepthwiseCon (None, 10, 10, 384)  3456        block_8_expand_relu[0][0]        
__________________________________________________________________________________________________
block_8_depthwise_BN (BatchNorm (None, 10, 10, 384)  1536        block_8_depthwise[0][0]          
__________________________________________________________________________________________________
block_8_depthwise_relu (ReLU)   (None, 10, 10, 384)  0           block_8_depthwise_BN[0][0]       
__________________________________________________________________________________________________
block_8_project (Conv2D)        (None, 10, 10, 64)   24576       block_8_depthwise_relu[0][0]     
__________________________________________________________________________________________________
block_8_project_BN (BatchNormal (None, 10, 10, 64)   256         block_8_project[0][0]            
__________________________________________________________________________________________________
block_8_add (Add)               (None, 10, 10, 64)   0           block_7_add[0][0]                
                                                                 block_8_project_BN[0][0]         
__________________________________________________________________________________________________
block_9_expand (Conv2D)         (None, 10, 10, 384)  24576       block_8_add[0][0]                
__________________________________________________________________________________________________
block_9_expand_BN (BatchNormali (None, 10, 10, 384)  1536        block_9_expand[0][0]             
__________________________________________________________________________________________________
block_9_expand_relu (ReLU)      (None, 10, 10, 384)  0           block_9_expand_BN[0][0]          
__________________________________________________________________________________________________
block_9_depthwise (DepthwiseCon (None, 10, 10, 384)  3456        block_9_expand_relu[0][0]        
__________________________________________________________________________________________________
block_9_depthwise_BN (BatchNorm (None, 10, 10, 384)  1536        block_9_depthwise[0][0]          
__________________________________________________________________________________________________
block_9_depthwise_relu (ReLU)   (None, 10, 10, 384)  0           block_9_depthwise_BN[0][0]       
__________________________________________________________________________________________________
block_9_project (Conv2D)        (None, 10, 10, 64)   24576       block_9_depthwise_relu[0][0]     
__________________________________________________________________________________________________
block_9_project_BN (BatchNormal (None, 10, 10, 64)   256         block_9_project[0][0]            
__________________________________________________________________________________________________
block_9_add (Add)               (None, 10, 10, 64)   0           block_8_add[0][0]                
                                                                 block_9_project_BN[0][0]         
__________________________________________________________________________________________________
block_10_expand (Conv2D)        (None, 10, 10, 384)  24576       block_9_add[0][0]                
__________________________________________________________________________________________________
block_10_expand_BN (BatchNormal (None, 10, 10, 384)  1536        block_10_expand[0][0]            
__________________________________________________________________________________________________
block_10_expand_relu (ReLU)     (None, 10, 10, 384)  0           block_10_expand_BN[0][0]         
__________________________________________________________________________________________________
block_10_depthwise (DepthwiseCo (None, 10, 10, 384)  3456        block_10_expand_relu[0][0]       
__________________________________________________________________________________________________
block_10_depthwise_BN (BatchNor (None, 10, 10, 384)  1536        block_10_depthwise[0][0]         
__________________________________________________________________________________________________
block_10_depthwise_relu (ReLU)  (None, 10, 10, 384)  0           block_10_depthwise_BN[0][0]      
__________________________________________________________________________________________________
block_10_project (Conv2D)       (None, 10, 10, 96)   36864       block_10_depthwise_relu[0][0]    
__________________________________________________________________________________________________
block_10_project_BN (BatchNorma (None, 10, 10, 96)   384         block_10_project[0][0]           
__________________________________________________________________________________________________
block_11_expand (Conv2D)        (None, 10, 10, 576)  55296       block_10_project_BN[0][0]        
__________________________________________________________________________________________________
block_11_expand_BN (BatchNormal (None, 10, 10, 576)  2304        block_11_expand[0][0]            
__________________________________________________________________________________________________
block_11_expand_relu (ReLU)     (None, 10, 10, 576)  0           block_11_expand_BN[0][0]         
__________________________________________________________________________________________________
block_11_depthwise (DepthwiseCo (None, 10, 10, 576)  5184        block_11_expand_relu[0][0]       
__________________________________________________________________________________________________
block_11_depthwise_BN (BatchNor (None, 10, 10, 576)  2304        block_11_depthwise[0][0]         
__________________________________________________________________________________________________
block_11_depthwise_relu (ReLU)  (None, 10, 10, 576)  0           block_11_depthwise_BN[0][0]      
__________________________________________________________________________________________________
block_11_project (Conv2D)       (None, 10, 10, 96)   55296       block_11_depthwise_relu[0][0]    
__________________________________________________________________________________________________
block_11_project_BN (BatchNorma (None, 10, 10, 96)   384         block_11_project[0][0]           
__________________________________________________________________________________________________
block_11_add (Add)              (None, 10, 10, 96)   0           block_10_project_BN[0][0]        
                                                                 block_11_project_BN[0][0]        
__________________________________________________________________________________________________
block_12_expand (Conv2D)        (None, 10, 10, 576)  55296       block_11_add[0][0]               
__________________________________________________________________________________________________
block_12_expand_BN (BatchNormal (None, 10, 10, 576)  2304        block_12_expand[0][0]            
__________________________________________________________________________________________________
block_12_expand_relu (ReLU)     (None, 10, 10, 576)  0           block_12_expand_BN[0][0]         
__________________________________________________________________________________________________
block_12_depthwise (DepthwiseCo (None, 10, 10, 576)  5184        block_12_expand_relu[0][0]       
__________________________________________________________________________________________________
block_12_depthwise_BN (BatchNor (None, 10, 10, 576)  2304        block_12_depthwise[0][0]         
__________________________________________________________________________________________________
block_12_depthwise_relu (ReLU)  (None, 10, 10, 576)  0           block_12_depthwise_BN[0][0]      
__________________________________________________________________________________________________
block_12_project (Conv2D)       (None, 10, 10, 96)   55296       block_12_depthwise_relu[0][0]    
__________________________________________________________________________________________________
block_12_project_BN (BatchNorma (None, 10, 10, 96)   384         block_12_project[0][0]           
__________________________________________________________________________________________________
block_12_add (Add)              (None, 10, 10, 96)   0           block_11_add[0][0]               
                                                                 block_12_project_BN[0][0]        
__________________________________________________________________________________________________
block_13_expand (Conv2D)        (None, 10, 10, 576)  55296       block_12_add[0][0]               
__________________________________________________________________________________________________
block_13_expand_BN (BatchNormal (None, 10, 10, 576)  2304        block_13_expand[0][0]            
__________________________________________________________________________________________________
block_13_expand_relu (ReLU)     (None, 10, 10, 576)  0           block_13_expand_BN[0][0]         
__________________________________________________________________________________________________
block_13_pad (ZeroPadding2D)    (None, 11, 11, 576)  0           block_13_expand_relu[0][0]       
__________________________________________________________________________________________________
block_13_depthwise (DepthwiseCo (None, 5, 5, 576)    5184        block_13_pad[0][0]               
__________________________________________________________________________________________________
block_13_depthwise_BN (BatchNor (None, 5, 5, 576)    2304        block_13_depthwise[0][0]         
__________________________________________________________________________________________________
block_13_depthwise_relu (ReLU)  (None, 5, 5, 576)    0           block_13_depthwise_BN[0][0]      
__________________________________________________________________________________________________
block_13_project (Conv2D)       (None, 5, 5, 160)    92160       block_13_depthwise_relu[0][0]    
__________________________________________________________________________________________________
block_13_project_BN (BatchNorma (None, 5, 5, 160)    640         block_13_project[0][0]           
__________________________________________________________________________________________________
block_14_expand (Conv2D)        (None, 5, 5, 960)    153600      block_13_project_BN[0][0]        
__________________________________________________________________________________________________
block_14_expand_BN (BatchNormal (None, 5, 5, 960)    3840        block_14_expand[0][0]            
__________________________________________________________________________________________________
block_14_expand_relu (ReLU)     (None, 5, 5, 960)    0           block_14_expand_BN[0][0]         
__________________________________________________________________________________________________
block_14_depthwise (DepthwiseCo (None, 5, 5, 960)    8640        block_14_expand_relu[0][0]       
__________________________________________________________________________________________________
block_14_depthwise_BN (BatchNor (None, 5, 5, 960)    3840        block_14_depthwise[0][0]         
__________________________________________________________________________________________________
block_14_depthwise_relu (ReLU)  (None, 5, 5, 960)    0           block_14_depthwise_BN[0][0]      
__________________________________________________________________________________________________
block_14_project (Conv2D)       (None, 5, 5, 160)    153600      block_14_depthwise_relu[0][0]    
__________________________________________________________________________________________________
block_14_project_BN (BatchNorma (None, 5, 5, 160)    640         block_14_project[0][0]           
__________________________________________________________________________________________________
block_14_add (Add)              (None, 5, 5, 160)    0           block_13_project_BN[0][0]        
                                                                 block_14_project_BN[0][0]        
__________________________________________________________________________________________________
block_15_expand (Conv2D)        (None, 5, 5, 960)    153600      block_14_add[0][0]               
__________________________________________________________________________________________________
block_15_expand_BN (BatchNormal (None, 5, 5, 960)    3840        block_15_expand[0][0]            
__________________________________________________________________________________________________
block_15_expand_relu (ReLU)     (None, 5, 5, 960)    0           block_15_expand_BN[0][0]         
__________________________________________________________________________________________________
block_15_depthwise (DepthwiseCo (None, 5, 5, 960)    8640        block_15_expand_relu[0][0]       
__________________________________________________________________________________________________
block_15_depthwise_BN (BatchNor (None, 5, 5, 960)    3840        block_15_depthwise[0][0]         
__________________________________________________________________________________________________
block_15_depthwise_relu (ReLU)  (None, 5, 5, 960)    0           block_15_depthwise_BN[0][0]      
__________________________________________________________________________________________________
block_15_project (Conv2D)       (None, 5, 5, 160)    153600      block_15_depthwise_relu[0][0]    
__________________________________________________________________________________________________
block_15_project_BN (BatchNorma (None, 5, 5, 160)    640         block_15_project[0][0]           
__________________________________________________________________________________________________
block_15_add (Add)              (None, 5, 5, 160)    0           block_14_add[0][0]               
                                                                 block_15_project_BN[0][0]        
__________________________________________________________________________________________________
block_16_expand (Conv2D)        (None, 5, 5, 960)    153600      block_15_add[0][0]               
__________________________________________________________________________________________________
block_16_expand_BN (BatchNormal (None, 5, 5, 960)    3840        block_16_expand[0][0]            
__________________________________________________________________________________________________
block_16_expand_relu (ReLU)     (None, 5, 5, 960)    0           block_16_expand_BN[0][0]         
__________________________________________________________________________________________________
block_16_depthwise (DepthwiseCo (None, 5, 5, 960)    8640        block_16_expand_relu[0][0]       
__________________________________________________________________________________________________
block_16_depthwise_BN (BatchNor (None, 5, 5, 960)    3840        block_16_depthwise[0][0]         
__________________________________________________________________________________________________
block_16_depthwise_relu (ReLU)  (None, 5, 5, 960)    0           block_16_depthwise_BN[0][0]      
__________________________________________________________________________________________________
block_16_project (Conv2D)       (None, 5, 5, 320)    307200      block_16_depthwise_relu[0][0]    
__________________________________________________________________________________________________
block_16_project_BN (BatchNorma (None, 5, 5, 320)    1280        block_16_project[0][0]           
__________________________________________________________________________________________________
Conv_1 (Conv2D)                 (None, 5, 5, 1280)   409600      block_16_project_BN[0][0]        
__________________________________________________________________________________________________
Conv_1_bn (BatchNormalization)  (None, 5, 5, 1280)   5120        Conv_1[0][0]                     
__________________________________________________________________________________________________
out_relu (ReLU)                 (None, 5, 5, 1280)   0           Conv_1_bn[0][0]                  
==================================================================================================
Total params: 2,257,984
Trainable params: 0
Non-trainable params: 2,257,984
__________________________________________________________________________________________________

 

分類ヘッドを追加する

特徴ブロックから予測を生成するために、特徴を画像毎に単一 1280-要素ベクトルに変換するために tf.keras.layers.GlobalAveragePlloing2d 層を使用して 5×5 空間的位置に渡り平均します。

global_average_layer = tf.keras.layers.GlobalAveragePooling2D()
feature_batch_average = global_average_layer(feature_batch)
print(feature_batch_average.shape)
(32, 1280)

その上にこれらの特徴を画像毎に単一の予測に変換するために tf.keras.layers.Dense 層を適用します。ここでは活性化関数は使用しないでください、何故ならばこの予測はロジット、あるいは生の予測値として扱われるからです。正の数はクラス 1 を予測し、負の数はクラス 0 を予測します。

prediction_layer = keras.layers.Dense(1)
prediction_batch = prediction_layer(feature_batch_average)
print(prediction_batch.shape)
(32, 1)

さて特徴抽出器、そしてこれらの 2 つの層を tf.keras.Sequential モデルを使用してスタックします :

model = tf.keras.Sequential([
  base_model,
  global_average_layer,
  prediction_layer
])

 

モデルをコンパイルする

モデルを訓練する前にそれをコンパイルしなければなりません。2 クラスありますので、binary cross-entropy 損失を使用します。

base_learning_rate = 0.0001
model.compile(optimizer=tf.keras.optimizers.RMSprop(lr=base_learning_rate),
              loss='binary_crossentropy',
              metrics=['accuracy'])
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
mobilenetv2_1.00_160 (Model) (None, 5, 5, 1280)        2257984   
_________________________________________________________________
global_average_pooling2d (Gl (None, 1280)              0         
_________________________________________________________________
dense (Dense)                (None, 1)                 1281      
=================================================================
Total params: 2,259,265
Trainable params: 1,281
Non-trainable params: 2,257,984
_________________________________________________________________

MobileNet の 2.5M パラメータは凍結されますが、Dense 層に 1.2K の訓練可能なパラメータがあります。これらは 2 つの tf.Variable オブジェクトの間で分割されます、重みとバイアスです。

len(model.trainable_variables)
2

 

モデルを訓練する

10 エポックの間の訓練後、 ~96% 精度を見るはずです。

num_train, num_val, num_test = (
  metadata.splits['train'].num_examples*weight/10
  for weight in SPLIT_WEIGHTS
)
initial_epochs = 10
steps_per_epoch = round(num_train)//BATCH_SIZE
validation_steps = 20

loss0,accuracy0 = model.evaluate(validation_batches, steps = validation_steps)
20/20 [==============================] - 3s 158ms/step - loss: 6.5439 - accuracy: 0.3844
print("initial loss: {:.2f}".format(loss0))
print("initial accuracy: {:.2f}".format(accuracy0))
initial loss: 6.54
initial accuracy: 0.38
history = model.fit(train_batches,
                    epochs=initial_epochs,
                    validation_data=validation_batches)
Epoch 1/10

W0614 17:12:16.199838 140109792941824 deprecation.py:323] From /home/kbuilder/.local/lib/python3.5/site-packages/tensorflow/python/ops/math_grad.py:1250: add_dispatch_support..wrapper (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.where in 2.0, which has the same broadcast rule as np.where

582/582 [==============================] - 47s 82ms/step - loss: 3.4220 - accuracy: 0.6083 - val_loss: 0.0000e+00 - val_accuracy: 0.0000e+00
Epoch 2/10
582/582 [==============================] - 42s 73ms/step - loss: 1.3558 - accuracy: 0.8257 - val_loss: 0.7131 - val_accuracy: 0.9060
Epoch 3/10
582/582 [==============================] - 42s 71ms/step - loss: 0.8893 - accuracy: 0.8828 - val_loss: 0.5687 - val_accuracy: 0.9302
Epoch 4/10
582/582 [==============================] - 42s 73ms/step - loss: 0.7114 - accuracy: 0.9048 - val_loss: 0.4878 - val_accuracy: 0.9358
Epoch 5/10
582/582 [==============================] - 42s 72ms/step - loss: 0.6170 - accuracy: 0.9160 - val_loss: 0.4194 - val_accuracy: 0.9427
Epoch 6/10
582/582 [==============================] - 43s 74ms/step - loss: 0.5632 - accuracy: 0.9238 - val_loss: 0.4293 - val_accuracy: 0.9435
Epoch 7/10
582/582 [==============================] - 42s 72ms/step - loss: 0.5184 - accuracy: 0.9290 - val_loss: 0.4691 - val_accuracy: 0.9431
Epoch 8/10
582/582 [==============================] - 42s 72ms/step - loss: 0.4957 - accuracy: 0.9343 - val_loss: 0.4045 - val_accuracy: 0.9539
Epoch 9/10
582/582 [==============================] - 42s 72ms/step - loss: 0.4657 - accuracy: 0.9387 - val_loss: 0.4143 - val_accuracy: 0.9522
Epoch 10/10
582/582 [==============================] - 41s 70ms/step - loss: 0.4482 - accuracy: 0.9413 - val_loss: 0.4193 - val_accuracy: 0.9556

 

学習カーブ

MobileNet V2 ベースモデルを固定特徴抽出器として使用するときの、訓練と検証精度 / 損失の学習カーブを見てみましょう。

acc = history.history['accuracy']
val_acc = history.history['val_accuracy']

loss = history.history['loss']
val_loss = history.history['val_loss']

plt.figure(figsize=(8, 8))
plt.subplot(2, 1, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.ylabel('Accuracy')
plt.ylim([min(plt.ylim()),1])
plt.title('Training and Validation Accuracy')

plt.subplot(2, 1, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.ylabel('Cross Entropy')
plt.ylim([0,1.0])
plt.title('Training and Validation Loss')
plt.xlabel('epoch')
plt.show()

Note: もし貴方が何故検証メトリクスが訓練メトリクスよりも明らかに良いのか不思議に思うのであれば、主要因は tf.keras.layers.BatchNormalizationtf.keras.layers.Dropout のような層が訓練の間の精度に影響するからです。それらは検証損失を計算するときには無効にされます。

程度は少ないですが、それはまた検証メトリクスがエポック後に評価される一方で訓練メトリクスがエポックのための平均を報告するからです、そのため検証メトリクスは僅かばかり長く訓練されたモデルを見ます。

 

再調整

私達の特徴抽出実験では、MobileNet V2 ベースモデルの上の 2, 3 層だけを訓練していました。事前訓練されたネットワークの重みは訓練の間に更新されませんでした。

より以上にパフォーマンスを増す一つの方法は 、貴方が追加した分類器の訓練と一緒に事前訓練されたモデルの上部の層の重みを訓練 (or「再調整」) することです。訓練過程は一般的な特徴マップから私達のデータセットに特に関連する特徴へと重みが調整されることを強制します。

Note: これは事前訓練されたモデルを非訓練可能に設定しながら top-level 分類器を訓練した後でのみ試されるべきです。もし貴方が事前訓練されたモデルの上にランダムに初期化された分類器を追加して総ての層を一緒に訓練することを試みる場合、勾配更新の大きさが (分類器からのランダム重みゆえに) 大きすぎて貴方の事前訓練されたモデルはそれが学習したことを総て単に忘れるでしょう。

更に、MobileNet モデル全体よりも小さい数の top 層の再調整を試みるべきです。殆どの畳み込みネットワークでは、層が高位になればなるほど、それはより特化されます。最初の 2, 3 の層は非常に単純で一般的な特徴を学習して、それは殆ど総てのタイプの画像に一般化されます。より高く行くほどに、特徴は次第に (モデルがその上で訓練された) データセット特有になります。再調整の目標は、一般的な学習を上書きすることではなく、これらの専門的な特徴を新しいデータで動作するように適応させることです。

 

モデルのトップ層を解凍する

貴方が行なう必要がある総てのことは base_model を解凍してボトム層を非訓練可能に設定することです。それから、モデルを再コンパイルするべきです (これらの変更が効果を持つようにするために必要です)、そして訓練を再開します。

base_model.trainable = True
# Let's take a look to see how many layers are in the base model
print("Number of layers in the base model: ", len(base_model.layers))

# Fine tune from this layer onwards
fine_tune_at = 100

# Freeze all the layers before the `fine_tune_at` layer
for layer in base_model.layers[:fine_tune_at]:
  layer.trainable =  False
Number of layers in the base model:  155

 

モデルをコンパイルする

遥かに低い訓練率 (= training rate) を使用してモデルをコンパイルします。

model.compile(loss='binary_crossentropy',
              optimizer = tf.keras.optimizers.RMSprop(lr=base_learning_rate/10),
              metrics=['accuracy'])
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
mobilenetv2_1.00_160 (Model) (None, 5, 5, 1280)        2257984   
_________________________________________________________________
global_average_pooling2d (Gl (None, 1280)              0         
_________________________________________________________________
dense (Dense)                (None, 1)                 1281      
=================================================================
Total params: 2,259,265
Trainable params: 1,863,873
Non-trainable params: 395,392
_________________________________________________________________
len(model.trainable_variables)
58

 

モデルの訓練を継続する

先に収束するために訓練したのであれば、これは数パーセントの更なる精度を貴方に得させるでしょう。

fine_tune_epochs = 10
total_epochs =  initial_epochs + fine_tune_epochs

history_fine = model.fit(train_batches,
                         epochs=total_epochs,
                         initial_epoch = initial_epochs,
                         validation_data=validation_batches)
Epoch 11/20
582/582 [==============================] - 79s 136ms/step - loss: 0.4317 - accuracy: 0.9486 - val_loss: 0.0000e+00 - val_accuracy: 0.0000e+00
Epoch 12/20
582/582 [==============================] - 53s 91ms/step - loss: 0.3406 - accuracy: 0.9684 - val_loss: 0.2794 - val_accuracy: 0.9724
Epoch 13/20
582/582 [==============================] - 50s 85ms/step - loss: 0.2951 - accuracy: 0.9788 - val_loss: 0.2575 - val_accuracy: 0.9707
Epoch 14/20
582/582 [==============================] - 50s 86ms/step - loss: 0.2792 - accuracy: 0.9814 - val_loss: 0.2487 - val_accuracy: 0.9716
Epoch 15/20
582/582 [==============================] - 50s 86ms/step - loss: 0.2684 - accuracy: 0.9821 - val_loss: 0.2992 - val_accuracy: 0.9694
Epoch 16/20
582/582 [==============================] - 51s 87ms/step - loss: 0.2610 - accuracy: 0.9827 - val_loss: 0.2544 - val_accuracy: 0.9703
Epoch 17/20
582/582 [==============================] - 51s 88ms/step - loss: 0.2487 - accuracy: 0.9831 - val_loss: 0.2494 - val_accuracy: 0.9711
Epoch 18/20
582/582 [==============================] - 54s 93ms/step - loss: 0.2424 - accuracy: 0.9839 - val_loss: 0.2645 - val_accuracy: 0.9694
Epoch 19/20
582/582 [==============================] - 54s 92ms/step - loss: 0.2370 - accuracy: 0.9843 - val_loss: 0.2479 - val_accuracy: 0.9711
Epoch 20/20
582/582 [==============================] - 50s 86ms/step - loss: 0.2336 - accuracy: 0.9847 - val_loss: 0.2738 - val_accuracy: 0.9698

MobileNet V2 ベースモデルの最後の 2, 3 層を再調整してその上の分類器を訓練するとき、訓練と検証精度 / 損失の学習カーブを見てみましょう。検証損失は訓練損失よりも遥かに高いので、何某かの overfitting を得ているかもしれません。

新しい訓練データセットは比較的小さくて元の MobileNet V2 データセットに類似しているので、某かの overfitting をまた得ているかもしれません。

再調整後にモデルは 98% 精度近くに到達します。

acc += history_fine.history['accuracy']
val_acc += history_fine.history['val_accuracy']

loss += history_fine.history['loss']
val_loss += history_fine.history['val_loss']
plt.figure(figsize=(8, 8))
plt.subplot(2, 1, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.ylim([0.8, 1])
plt.plot([initial_epochs-1,initial_epochs-1],
          plt.ylim(), label='Start Fine Tuning')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(2, 1, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.ylim([0, 1.0])
plt.plot([initial_epochs-1,initial_epochs-1],
         plt.ylim(), label='Start Fine Tuning')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.xlabel('epoch')
plt.show()

 

要約 :

特徴抽出のために事前訓練されたモデルを使用する: 小さいデータセットで作業するとき、同じドメインのより巨大なデータセット上で訓練されたモデルにより学習された特徴を活用することは一般的です。これは事前訓練されたモデルをインスタンス化して完全結合分類器を上に追加することにより成されます。事前訓練されたモデルは「凍結」されて訓練の間分類器の重みだけが更新されます。この場合、畳み込みベースが各画像に関係する総ての特徴を抽出してそして特徴のこれらのセットが与えられたとき画像クラスを決定する分類器を訓練します。

事前訓練されたモデルを再調整する: パフォーマンスを更に改良するために、事前訓練されたモデルの top-level 層を再調整を通して新しいデータセットに再目的化することを望むかもしれません。この場合、データセットに固有の高位な特徴を学習するように重みを調整します。このテクニックは通常は、訓練データセットが巨大で (事前訓練されたモデルがその上で訓練された) 元のデータセットに非常に類似しているときに限り推奨されます。

 

以上



TensorFlow 2.0 Beta : Tutorials : 画像 :- 畳み込みニューラルネットワーク

TensorFlow 2.0 Beta : Beginner Tutorials : 画像 :- 畳み込みニューラルネットワーク (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 06/25/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Beta – Beginner Tutorials – Images の以下のページを翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

画像 :- 畳み込みニューラルネットワーク

このチュートリアルは MNIST 数字を分類するための単純な 畳み込みニューラルネットワーク (CNN) の訓練を実演します。この単純なネットワークは MNIST テストセット上で 99% 超の精度を獲得します。このチュートリアルは Keras Sequential API を使用しますので、モデルの作成と訓練は単にコードの数行を取るだけです。

 

Import TensorFlow

from __future__ import absolute_import, division, print_function, unicode_literals

!pip install -q tensorflow-gpu==2.0.0-beta1
import tensorflow as tf

from tensorflow.keras import datasets, layers, models

 

MNIST データセットをダウンロードして準備する

(train_images, train_labels), (test_images, test_labels) = datasets.mnist.load_data()

train_images = train_images.reshape((60000, 28, 28, 1))
test_images = test_images.reshape((10000, 28, 28, 1))

# Normalize pixel values to be between 0 and 1
train_images, test_images = train_images / 255.0, test_images / 255.0

 

畳み込みベースを作成する

下の 6 行のコードは一般的なパターン: Conv2DMaxPooling2D 層のスタックを使用して畳み込みベースを作成します。

入力として、CNN は shape (image_height, image_width, color_channels) の tensor を取り、バッチサイズは無視します。カラーチャネルに馴染みがないのであれば、カラー画像が 3 つ (R, G, B) を持つところを MNIST は一つ持ちます (何故ならば画像がグレースケールだからです)。この例では、MNIST 画像のフォーマットである shape (28, 28, 1) の入力を処理する CNN を構成します。
引数 input_shape を最初の層に渡すことによりこれを成します。

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))

ここまでのモデルの構造を表示しましょう。

model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 26, 26, 32)        320       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 13, 13, 32)        0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 11, 11, 64)        18496     
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 5, 5, 64)          0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 3, 3, 64)          36928     
=================================================================
Total params: 55,744
Trainable params: 55,744
Non-trainable params: 0
_________________________________________________________________

上で、総ての Conv2D と MaxPooling2D 層の出力が shape (height, width, channels) の 3D tensor であることを見ることができます。幅と高さの次元はネットワークで深くなるにつれて縮小する傾向にあります。各 Conv2D 層の出力チャネル数は最初の引数 (e.g., 32 or 64) により制御されます。典型的には、幅と高さが縮小するにつれて、各 Conv2D 層でより多くの出力チャネルを追加する能力が (計算的に) あります。

 

上に Dense 層を追加する

モデルを完成するために、分類を遂行するために (shape (3, 3, 64) の) 畳み込みベースからの最後の出力 tensor を一つかそれ以上の Dense 層に供給します。Dense 層は入力としてベクトル (それは 1D です) を取りますが、その一方で現在の出力は 3D tensor です。最初に、3D 出力を 1D に平坦化 (or 展開) して、それから一つかそれ以上の Dense 層を上に追加します。MNIST は 10 出力クラスを持ちますので、10 出力と softmax 活性を持つ最後の Dense 層を使用します。

model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))

ここにモデルの完全なアーキテクチャがあります。

model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 26, 26, 32)        320       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 13, 13, 32)        0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 11, 11, 64)        18496     
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 5, 5, 64)          0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 3, 3, 64)          36928     
_________________________________________________________________
flatten (Flatten)            (None, 576)               0         
_________________________________________________________________
dense (Dense)                (None, 64)                36928     
_________________________________________________________________
dense_1 (Dense)              (None, 10)                650       
=================================================================
Total params: 93,322
Trainable params: 93,322
Non-trainable params: 0
_________________________________________________________________

見れるように、2 つの Dense 層を通る前に (3, 3, 64) 出力は shape (576) のベクトルに平坦化されます。

 

モデルをコンパイルして訓練する

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

model.fit(train_images, train_labels, epochs=5)
WARNING: Logging before flag parsing goes to stderr.
W0614 17:10:23.754953 139724426172160 deprecation.py:323] From /home/kbuilder/.local/lib/python3.5/site-packages/tensorflow/python/ops/math_grad.py:1250: add_dispatch_support..wrapper (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.where in 2.0, which has the same broadcast rule as np.where

Train on 60000 samples
Epoch 1/5
60000/60000 [==============================] - 9s 153us/sample - loss: 0.1463 - accuracy: 0.9544
Epoch 2/5
60000/60000 [==============================] - 7s 116us/sample - loss: 0.0465 - accuracy: 0.9856
Epoch 3/5
60000/60000 [==============================] - 7s 115us/sample - loss: 0.0330 - accuracy: 0.9896
Epoch 4/5
60000/60000 [==============================] - 8s 135us/sample - loss: 0.0262 - accuracy: 0.9917
Epoch 5/5
60000/60000 [==============================] - 7s 123us/sample - loss: 0.0194 - accuracy: 0.9935

<tensorflow.python.keras.callbacks.History at 0x7f1399d8f710>

 

モデルを評価する

test_loss, test_acc = model.evaluate(test_images, test_labels)
10000/10000 [==============================] - 1s 71us/sample - loss: 0.0281 - accuracy: 0.9921
print(test_acc)
0.9921
 

以上



TensorFlow 2.0 Beta : Tutorials : ML 基本 :- モデルをセーブしてリストアする

TensorFlow 2.0 Beta : Beginner Tutorials : ML 基本 :- モデルをセーブしてリストアする (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 06/23/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Beta – Beginner Tutorials – ML basics の以下のページを翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

ML 基本 :- モデルをセーブしてリストアする

モデルの進捗は訓練の間に — そして後に — セーブすることが可能です。これは、モデルが (訓練を) 中止したところから再開できて長い訓練時間を回避できることを意味します。セービングはまた貴方がモデルを共有して他の人は貴方のワークを再作成できることもまた意味します。研究モデルとテクニックを公開するとき、多くの機械学習実践者は以下を共有します :

  • モデルを作成するためのコード、そして
  • モデルのために訓練された重み、またはパラメータ

このデータの共有は他の人たちがモデルがどのように動作するかを理解して新しいデータで彼ら自身がそれを試す手助けとなります。

Caution: 信頼できない (untrusted) コードに注意してください — TensorFlow モデルはコードです。詳細は Using TensorFlow Securely を見てください。

 

オプション

TensorFlow モデルをセーブする異なる方法があります — 貴方が使用している API に依拠します。このガイドは tf.keras, TensorFlow でモデルを構築して訓練するための高位 API を使用します。他のアプローチについては、TensorFlow Save and Restore ガイドや Saving in eager を見てください。

 

セットアップ

インストールとインポート

TensorFlow と依存関係をインストールしてインポートします :

!pip install -q h5py pyyaml

 

サンプル・データセットを取得する

重みのセーブを実演するために MNIST データセット を使用してモデルを訓練します。これらのデモの実行をスピードアップするために、最初の 1000 サンプルだけを使用します :

from __future__ import absolute_import, division, print_function, unicode_literals

import os

!pip install -q tensorflow==2.0.0-beta1
import tensorflow as tf
from tensorflow import keras

tf.__version__
'2.0.0-beta1'
(train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.mnist.load_data()

train_labels = train_labels[:1000]
test_labels = test_labels[:1000]

train_images = train_images[:1000].reshape(-1, 28 * 28) / 255.0
test_images = test_images[:1000].reshape(-1, 28 * 28) / 255.0

 

モデルを定義する

重みのセーブとロードを実演するために使用する単純なモデルを構築しましょう。

# Returns a short sequential model
def create_model():
  model = tf.keras.models.Sequential([
    keras.layers.Dense(512, activation='relu', input_shape=(784,)),
    keras.layers.Dropout(0.2),
    keras.layers.Dense(10, activation='softmax')
  ])

  model.compile(optimizer='adam',
                loss='sparse_categorical_crossentropy',
                metrics=['accuracy'])

  return model


# Create a basic model instance
model = create_model()
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense (Dense)                (None, 512)               401920    
_________________________________________________________________
dropout (Dropout)            (None, 512)               0         
_________________________________________________________________
dense_1 (Dense)              (None, 10)                5130      
=================================================================
Total params: 407,050
Trainable params: 407,050
Non-trainable params: 0
_________________________________________________________________

 

訓練の間にチェックポイントをセーブする

主なユースケースは訓練の間と最後にチェックポイントを自動的にセーブすることです。このように訓練されたモデルをそれを再訓練しなくても使用できたり、あるいは — 訓練プロセスが中断された場合には — 中止したところから訓練を選択することができます。

tf.keras.callbacks.ModelCheckpoint はこのタスクを遂行する callback です。この callback はチェックポインティングを構成するために 2, 3 の引数を取ります。

 

Checkpoint callback 使用方法

モデルを訓練してそれを ModelCheckpoint callback に渡します :

checkpoint_path = "training_1/cp.ckpt"
checkpoint_dir = os.path.dirname(checkpoint_path)

# Create checkpoint callback
cp_callback = tf.keras.callbacks.ModelCheckpoint(checkpoint_path,
                                                 save_weights_only=True,
                                                 verbose=1)

model = create_model()

model.fit(train_images, train_labels,  epochs = 10,
          validation_data = (test_images,test_labels),
          callbacks = [cp_callback])  # pass callback to training

# This may generate warnings related to saving the state of the optimizer.
# These warnings (and similar warnings throughout this notebook)
# are in place to discourage outdated usage, and can be ignored.
WARNING: Logging before flag parsing goes to stderr.
W0614 17:44:59.997754 139650494461696 deprecation.py:323] From /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/ops/math_grad.py:1250: add_dispatch_support..wrapper (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.where in 2.0, which has the same broadcast rule as np.where

Train on 1000 samples, validate on 1000 samples
Epoch 1/10
 992/1000 [============================>.] - ETA: 0s - loss: 1.1820 - accuracy: 0.6512
Epoch 00001: saving model to training_1/cp.ckpt
1000/1000 [==============================] - 1s 502us/sample - loss: 1.1799 - accuracy: 0.6520 - val_loss: 0.7200 - val_accuracy: 0.7890
Epoch 2/10
 544/1000 [===============>..............] - ETA: 0s - loss: 0.4237 - accuracy: 0.8787
Epoch 00002: saving model to training_1/cp.ckpt
1000/1000 [==============================] - 0s 170us/sample - loss: 0.4251 - accuracy: 0.8800 - val_loss: 0.5577 - val_accuracy: 0.8340
Epoch 3/10
 544/1000 [===============>..............] - ETA: 0s - loss: 0.3251 - accuracy: 0.9118
Epoch 00003: saving model to training_1/cp.ckpt
1000/1000 [==============================] - 0s 171us/sample - loss: 0.3021 - accuracy: 0.9210 - val_loss: 0.5011 - val_accuracy: 0.8350
Epoch 4/10
 544/1000 [===============>..............] - ETA: 0s - loss: 0.2239 - accuracy: 0.9485
Epoch 00004: saving model to training_1/cp.ckpt
1000/1000 [==============================] - 0s 170us/sample - loss: 0.2135 - accuracy: 0.9520 - val_loss: 0.4270 - val_accuracy: 0.8640
Epoch 5/10
 544/1000 [===============>..............] - ETA: 0s - loss: 0.1613 - accuracy: 0.9669
Epoch 00005: saving model to training_1/cp.ckpt
1000/1000 [==============================] - 0s 167us/sample - loss: 0.1620 - accuracy: 0.9630 - val_loss: 0.4244 - val_accuracy: 0.8590
Epoch 6/10
 576/1000 [================>.............] - ETA: 0s - loss: 0.1253 - accuracy: 0.9774
Epoch 00006: saving model to training_1/cp.ckpt
1000/1000 [==============================] - 0s 161us/sample - loss: 0.1215 - accuracy: 0.9780 - val_loss: 0.4317 - val_accuracy: 0.8520
Epoch 7/10
 576/1000 [================>.............] - ETA: 0s - loss: 0.0925 - accuracy: 0.9844
Epoch 00007: saving model to training_1/cp.ckpt
1000/1000 [==============================] - 0s 163us/sample - loss: 0.0903 - accuracy: 0.9860 - val_loss: 0.4201 - val_accuracy: 0.8600
Epoch 8/10
 576/1000 [================>.............] - ETA: 0s - loss: 0.0642 - accuracy: 0.9931
Epoch 00008: saving model to training_1/cp.ckpt
1000/1000 [==============================] - 0s 158us/sample - loss: 0.0640 - accuracy: 0.9930 - val_loss: 0.4130 - val_accuracy: 0.8600
Epoch 9/10
 576/1000 [================>.............] - ETA: 0s - loss: 0.0511 - accuracy: 0.9965
Epoch 00009: saving model to training_1/cp.ckpt
1000/1000 [==============================] - 0s 159us/sample - loss: 0.0497 - accuracy: 0.9980 - val_loss: 0.4306 - val_accuracy: 0.8650
Epoch 10/10
 576/1000 [================>.............] - ETA: 0s - loss: 0.0386 - accuracy: 1.0000
Epoch 00010: saving model to training_1/cp.ckpt
1000/1000 [==============================] - 0s 165us/sample - loss: 0.0423 - accuracy: 0.9990 - val_loss: 0.4258 - val_accuracy: 0.8640

<tensorflow.python.keras.callbacks.History at 0x7f028ee96e80>

これは、各エポックの最後に更新される TensorFlow チェックポイント・ファイルの単一のコレクションを作成します :

!ls {checkpoint_dir}
checkpoint  cp.ckpt.data-00000-of-00001  cp.ckpt.index

新しい、未訓練のモデルを作成します。重みだけからモデルをリストアするときは、元のモデルと同じアーキテクチャを持つモデルを持たなければなりません。それは同じモデル・アーキテクチャですから、それがモデルの異なるインスタンスであるにもかかわらず重みを共有することができます。

さて未使用の、未訓練のモデルを再構築して、それをテストセット上で評価しましょう。未訓練モデルは偶然レベル (~10% 精度) で遂行します :

model = create_model()

loss, acc = model.evaluate(test_images, test_labels)
print("Untrained model, accuracy: {:5.2f}%".format(100*acc))
1000/1000 [==============================] - 0s 104us/sample - loss: 2.3691 - accuracy: 0.0700
Untrained model, accuracy:  7.00%

それからチェックポイントから重みをロードして、再評価します :

model.load_weights(checkpoint_path)
loss,acc = model.evaluate(test_images, test_labels)
print("Restored model, accuracy: {:5.2f}%".format(100*acc))
1000/1000 [==============================] - 0s 46us/sample - loss: 0.4258 - accuracy: 0.8640
Restored model, accuracy: 86.40%

 

Checkpoint callback オプション

callback は結果としてのチェックポイントに一意な名前を与えて、チェックポイントする頻度を調整するために幾つかのオプションを提供します。

新しいモデルを訓練して、一意に名前付けられたチェックポイントを 5 エポック毎に一度セーブします :

# include the epoch in the file name. (uses `str.format`)
checkpoint_path = "training_2/cp-{epoch:04d}.ckpt"
checkpoint_dir = os.path.dirname(checkpoint_path)

cp_callback = tf.keras.callbacks.ModelCheckpoint(
    checkpoint_path, verbose=1, save_weights_only=True,
    # Save weights, every 5-epochs.
    period=5)

model = create_model()
model.save_weights(checkpoint_path.format(epoch=0))
model.fit(train_images, train_labels,
          epochs = 50, callbacks = [cp_callback],
          validation_data = (test_images,test_labels),
          verbose=0)
W0614 17:45:03.020097 139650494461696 callbacks.py:859] `period` argument is deprecated. Please use `save_freq` to specify the frequency in number of samples seen.


Epoch 00005: saving model to training_2/cp-0005.ckpt

Epoch 00010: saving model to training_2/cp-0010.ckpt

Epoch 00015: saving model to training_2/cp-0015.ckpt

Epoch 00020: saving model to training_2/cp-0020.ckpt

Epoch 00025: saving model to training_2/cp-0025.ckpt

Epoch 00030: saving model to training_2/cp-0030.ckpt

Epoch 00035: saving model to training_2/cp-0035.ckpt

Epoch 00040: saving model to training_2/cp-0040.ckpt

Epoch 00045: saving model to training_2/cp-0045.ckpt

Epoch 00050: saving model to training_2/cp-0050.ckpt

<tensorflow.python.keras.callbacks.History at 0x7f02c20f4f60>

さて、結果としてのチェックポイントを見て最新のものを選択します :

! ls {checkpoint_dir}
checkpoint            cp-0025.ckpt.index
cp-0000.ckpt.data-00000-of-00001  cp-0030.ckpt.data-00000-of-00001
cp-0000.ckpt.index        cp-0030.ckpt.index
cp-0005.ckpt.data-00000-of-00001  cp-0035.ckpt.data-00000-of-00001
cp-0005.ckpt.index        cp-0035.ckpt.index
cp-0010.ckpt.data-00000-of-00001  cp-0040.ckpt.data-00000-of-00001
cp-0010.ckpt.index        cp-0040.ckpt.index
cp-0015.ckpt.data-00000-of-00001  cp-0045.ckpt.data-00000-of-00001
cp-0015.ckpt.index        cp-0045.ckpt.index
cp-0020.ckpt.data-00000-of-00001  cp-0050.ckpt.data-00000-of-00001
cp-0020.ckpt.index        cp-0050.ckpt.index
cp-0025.ckpt.data-00000-of-00001
latest = tf.train.latest_checkpoint(checkpoint_dir)
latest
'training_2/cp-0050.ckpt'

Note: デフォルトの tensorflow フォーマットは 5 つの直近のチェックポイントだけをセーブします。

テストするためには、モデルをリセットして最新のチェックポイントをロードします :

model = create_model()
model.load_weights(latest)
loss, acc = model.evaluate(test_images, test_labels)
print("Restored model, accuracy: {:5.2f}%".format(100*acc))
1000/1000 [==============================] - 0s 100us/sample - loss: 0.4908 - accuracy: 0.8760
Restored model, accuracy: 87.60%

 

これらのファイルは何でしょう?

上のコードは重みを (訓練された重みだけをバイナリ形式で含む) チェックポイント 形式ファイルのコレクションにストアします。チェックポイントは次を含みます : * モデルの重みを含む一つまたはそれ以上のシャード。* どの重みがどのシャードにストアされているかを指し示すインデックスファイル。

単一のマシン上でモデルを訓練しているだけであれば、サフィックス: .data-00000-of-00001 を持つ一つのシャードを持つでしょう。

 

重みを手動でセーブする

上で重みをどのようにモデルにロードするかを見ました。

手動で重みをセーブするのは同様に単純で、Model.save_weights メソッドを使用します。

# Save the weights
model.save_weights('./checkpoints/my_checkpoint')

# Restore the weights
model = create_model()
model.load_weights('./checkpoints/my_checkpoint')

loss,acc = model.evaluate(test_images, test_labels)
print("Restored model, accuracy: {:5.2f}%".format(100*acc))
1000/1000 [==============================] - 0s 100us/sample - loss: 0.4908 - accuracy: 0.8760
Restored model, accuracy: 87.60%

 

モデル全体をセーブする

モデルと optimizer はそれらの状態 (重みと変数) とモデル構成 (= configuration) の両者を含むファイルにセーブできます。これはモデルをエクスポートしてそれは元の python コードへのアクセスなしに使用できます。optimizer-状態がリカバーされますので正確に貴方がやめたところから訓練を再開することさえできます。

完全に機能するモデルのセーブは非常に有用です — それらを TensorFlow.js にロード (HDF5, Saved Model) してから web ブラウザでそれらを訓練して実行することができます、あるいは TensorFlow Lite を使用して (HDF5, Saved Model) モバイルデバイス上で実行するためそれらを変換できます。

 

HDF5 ファイルとして

Keras は HDF5 標準を使用した基本的なセーブ・フォーマットを提供します。私達の目的のためには、セーブされたモデルは単一のバイナリ・ブロブとして扱うことができます。

model = create_model()

model.fit(train_images, train_labels, epochs=5)

# Save entire model to a HDF5 file
model.save('my_model.h5')
W0614 17:45:12.451796 139650494461696 util.py:244] Unresolved object in checkpoint: (root).optimizer.iter
W0614 17:45:12.453363 139650494461696 util.py:244] Unresolved object in checkpoint: (root).optimizer.beta_1
W0614 17:45:12.454297 139650494461696 util.py:244] Unresolved object in checkpoint: (root).optimizer.beta_2
W0614 17:45:12.455039 139650494461696 util.py:244] Unresolved object in checkpoint: (root).optimizer.decay
W0614 17:45:12.455814 139650494461696 util.py:244] Unresolved object in checkpoint: (root).optimizer.learning_rate
W0614 17:45:12.456508 139650494461696 util.py:252] A checkpoint was restored (e.g. tf.train.Checkpoint.restore or tf.keras.Model.load_weights) but not all checkpointed values were used. See above for specific issues. Use expect_partial() on the load status object, e.g. tf.train.Checkpoint.restore(...).expect_partial(), to silence these warnings, or use assert_consumed() to make the check explicit. See https://www.tensorflow.org/alpha/guide/checkpoints#loading_mechanics for details.
W0614 17:45:12.459612 139650494461696 util.py:244] Unresolved object in checkpoint: (root).optimizer.iter
W0614 17:45:12.460473 139650494461696 util.py:244] Unresolved object in checkpoint: (root).optimizer.beta_1
W0614 17:45:12.461083 139650494461696 util.py:244] Unresolved object in checkpoint: (root).optimizer.beta_2
W0614 17:45:12.461607 139650494461696 util.py:244] Unresolved object in checkpoint: (root).optimizer.decay
W0614 17:45:12.462147 139650494461696 util.py:244] Unresolved object in checkpoint: (root).optimizer.learning_rate
W0614 17:45:12.463938 139650494461696 util.py:252] A checkpoint was restored (e.g. tf.train.Checkpoint.restore or tf.keras.Model.load_weights) but not all checkpointed values were used. See above for specific issues. Use expect_partial() on the load status object, e.g. tf.train.Checkpoint.restore(...).expect_partial(), to silence these warnings, or use assert_consumed() to make the check explicit. See https://www.tensorflow.org/alpha/guide/checkpoints#loading_mechanics for details.

Train on 1000 samples
Epoch 1/5
1000/1000 [==============================] - 0s 259us/sample - loss: 1.1410 - accuracy: 0.6710
Epoch 2/5
1000/1000 [==============================] - 0s 103us/sample - loss: 0.4294 - accuracy: 0.8760
Epoch 3/5
1000/1000 [==============================] - 0s 102us/sample - loss: 0.2867 - accuracy: 0.9170
Epoch 4/5
1000/1000 [==============================] - 0s 100us/sample - loss: 0.2247 - accuracy: 0.9420
Epoch 5/5
1000/1000 [==============================] - 0s 96us/sample - loss: 0.1540 - accuracy: 0.9670

さてそのファイルからモデルを再作成します :

# Recreate the exact same model, including weights and optimizer.
new_model = keras.models.load_model('my_model.h5')
new_model.summary()
Model: "sequential_6"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_12 (Dense)             (None, 512)               401920    
_________________________________________________________________
dropout_6 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_13 (Dense)             (None, 10)                5130      
=================================================================
Total params: 407,050
Trainable params: 407,050
Non-trainable params: 0
_________________________________________________________________

その精度を確認します :

loss, acc = new_model.evaluate(test_images, test_labels)
print("Restored model, accuracy: {:5.2f}%".format(100*acc))
1000/1000 [==============================] - 0s 106us/sample - loss: 0.4157 - accuracy: 0.8630
Restored model, accuracy: 86.30%

このテクニックは総てをセーブします :

  • 重み値。
  • モデルの構成 (= configuration) (アーキテクチャ)
  • optimizer 構成

Keras はアーキテクチャを調べてモデルをセーブします。現在、TensorFlow optimizer (from tf.train) をセーブすることはできません。それらを使用するときはモデルをロードした後で再コンパイルする必要があり、そして optimizer の状態を失うでしょう。

 

saved_model として

Caution: tf.keras モデルをセーブするこのメソッドは実験的で将来的なバージョンでは変更されるかもしれません。

新しいモデルを構築します :

model = create_model()

model.fit(train_images, train_labels, epochs=5)
Train on 1000 samples
Epoch 1/5
1000/1000 [==============================] - 0s 252us/sample - loss: 1.1906 - accuracy: 0.6560
Epoch 2/5
1000/1000 [==============================] - 0s 98us/sample - loss: 0.4405 - accuracy: 0.8700
Epoch 3/5
1000/1000 [==============================] - 0s 98us/sample - loss: 0.3088 - accuracy: 0.9220
Epoch 4/5
1000/1000 [==============================] - 0s 96us/sample - loss: 0.2122 - accuracy: 0.9480
Epoch 5/5
1000/1000 [==============================] - 0s 91us/sample - loss: 0.1579 - accuracy: 0.9650

<tensorflow.python.keras.callbacks.History at 0x7f02c1609898>

saved_model を作成して、それをタイムスタンプされたディレクトリに置きます :

import time
saved_model_path = "./saved_models/{}".format(int(time.time()))

tf.keras.experimental.export_saved_model(model, saved_model_path)
saved_model_path
W0614 17:45:15.955815 139650494461696 deprecation.py:323] From /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/saved_model/signature_def_utils_impl.py:253: build_tensor_info (from tensorflow.python.saved_model.utils_impl) is deprecated and will be removed in a future version.
Instructions for updating:
This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.utils.build_tensor_info or tf.compat.v1.saved_model.build_tensor_info.
W0614 17:45:15.957547 139650494461696 export_utils.py:182] Export includes no default signature!
W0614 17:45:16.200236 139650494461696 export_utils.py:182] Export includes no default signature!

'./saved_models/1560534315'

貴方の saved model をリスティングします :

!ls saved_models/
1560534315

saved model から新鮮な (= fresh) keras モデルを再ロードします。

new_model = tf.keras.experimental.load_from_saved_model(saved_model_path)
new_model.summary()
Model: "sequential_7"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_14 (Dense)             (None, 512)               401920    
_________________________________________________________________
dropout_7 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_15 (Dense)             (None, 10)                5130      
=================================================================
Total params: 407,050
Trainable params: 407,050
Non-trainable params: 0
_________________________________________________________________

復元されたモデルを実行します。

model.predict(test_images).shape
(1000, 10)
# The model has to be compiled before evaluating.
# This step is not required if the saved model is only being deployed.

new_model.compile(optimizer=model.optimizer,  # keep the optimizer that was loaded
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

# Evaluate the restored model.
loss, acc = new_model.evaluate(test_images, test_labels)
print("Restored model, accuracy: {:5.2f}%".format(100*acc))
1000/1000 [==============================] - 0s 99us/sample - loss: 0.4428 - accuracy: 0.8500
Restored model, accuracy: 85.00%

 

What’s Next

tf.keras によるセーブとロードへの簡単なガイドでした。

  • tf.keras ガイド は tf.keras によるモデルのセーブとロードについて更に示します。
  • eager execution の間のセーブについては Saving in eager を見てください。
  • Save and Restore ガイドは TensorFlow セービングについての下位詳細を持ちます。
 

以上



TensorFlow 2.0 Beta : Tutorials : ML 基本 :- overfitting と underfitting を調査する

TensorFlow 2.0 Beta : Beginner Tutorials : ML 基本 :- overfitting と underfitting を調査する (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 06/23/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Beta – Beginner Tutorials – ML basics の以下のページを翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

ML 基本 :- overfitting と underfitting を調査する

いつものように、このサンプルのコードは tf.keras API を使用します、これについては TensorFlow Keras guide で更に学習できます。

前の例の両者 — 映画レビューの分類と燃費効率の予測 — では、検証データ上のモデルの精度が幾つかのエポックの訓練後に最大になり、それから減少し始めることを見ました。

換言すれば、モデルは訓練データに overfit しています。overfitting にどのように対処するかを学ぶことは重要です。訓練セット上で高い精度を獲得することはしばしば可能ですが、本当に望むことはテストデータ (あるいは前にまだ見ていないデータ) に上手く一般化されたモデルを開発することです。

overfitting の反対は underfitting です。underfitting は テストデータ上でまだ改善の余地があるときに発生します。これは幾つかの理由で起こります : モデルが十分にパワフルでない場合、過剰に正則化されている、あるいは単に十分に長く訓練されていない場合。これはネットワークが訓練データ内の関連するパターンを学習していないことを意味しています。

けれども長すぎる訓練をする場合、モデルは overfit し始めて訓練データからテストデータに一般化されないパターンを学習するでしょう。上手くバランスを取る必要があります。どのように適切な数のエポックの間訓練するかを理解することは下で探究するように有用なスキルです。

overfitting を回避するためには、最善の解法はより多くの訓練データを使用することです。より多くのデータ上で訓練されたモデルは自然により良く一般化されます。それが最早可能ではないとき、次善の解法は正則化のようなテクニックを使用することです。これらはモデルがストアできる情報の量とタイプに制約を課します。ネットワークが小さい数のパターンを記憶するだけの余裕がある場合、最適化プロセスはそれに最も目立つパターンに注目するように強制します、これは上手く一般化するより良い可能性を持ちます。

このノートブックでは、2 つの一般的な正則化テクニックを探究します — 重み正則化と dropout です — そしてそれらを使用して IMDB 映画レビュー分類ノートブックを改良します。

from __future__ import absolute_import, division, print_function, unicode_literals

!pip install -q tensorflow==2.0.0-beta1
import tensorflow as tf
from tensorflow import keras

import numpy as np
import matplotlib.pyplot as plt

print(tf.__version__)
2.0.0-beta1

 

IMDB データセットをダウンロードする

前のノートブックのように埋め込みを使用するのではなく、ここではセンテンスを multi-hot エンコードします。このモデルは訓練セットに対して迅速に overfit します。それは overfitting が発生するとき、それとどのように戦うかを実演するために使用されます。

リストの multi-hot エンコーディングはそれらを 0 と 1 のベクトルに変えることを意味します。具体的には、これは例えばシークエンス [3, 5] を (1 となる) インデックス 3 と 5 を除いて総て 0 の 10,000 次元ベクトルに変えることを意味します。

NUM_WORDS = 10000

(train_data, train_labels), (test_data, test_labels) = keras.datasets.imdb.load_data(num_words=NUM_WORDS)

def multi_hot_sequences(sequences, dimension):
    # Create an all-zero matrix of shape (len(sequences), dimension)
    results = np.zeros((len(sequences), dimension))
    for i, word_indices in enumerate(sequences):
        results[i, word_indices] = 1.0  # set specific indices of results[i] to 1s
    return results


train_data = multi_hot_sequences(train_data, dimension=NUM_WORDS)
test_data = multi_hot_sequences(test_data, dimension=NUM_WORDS)

結果としての multi-hot ベクトルの一つを見てみましょう。単語インデックスは頻度でソートされますので、プロットで見て取れるように、インデックス 0 の近くにはより多くの 1-値があることが予想されます :

plt.plot(train_data[0])
[]

 

overfitting を実演する

overfitting を回避する最も単純な方法はモデルのサイズ, i.e. モデルの学習可能なパラメータの数 (これは層数と層毎のユニット数により決定されます) を減じることです。深層学習では、モデルの学習可能なパラメータ数はしばしばモデルの “capacity” として参照されます。直感的には、より多くのパラメータを持つモデルはより多くの “記憶 capacity” を持ちそしてそれ故に訓練サンプルとターゲットの間の完全な辞書ライクなマッピング、どのような一般化パワーもないマッピングを容易に学習可能です、しかしこれは以前に見ていないデータ上で予測を行なうときに役に立ちません。

これを常に念頭に置いてください : 深層学習モデルは訓練データに fit するには良い傾向がありますが、実際の挑戦は一般化であり、fitting ではありません。

その一方で、ネットワークが制限された記憶リソースを持つ場合には、マッピングを容易には学習することができないでしょう。その損失を最小化するためには、より予測力を持つ圧縮された (= compressed) 表現を学習しなければなりません。同時に、モデルを小さくし過ぎれば、それは訓練データに fit することが困難になるでしょう。これは “too much capacity” と “not enough capacity” の間のバランスです。

不幸なことに、(層の数や各層のための正しいサイズの視点から) モデルの正しいサイズやアーキテクチャを決定するための魔法の公式はありません。異なるアーキテクチャのシリーズを使用して実験しなければなりません。

適切なモデルサイズを見つけるためには、比較的少ない層とパラメータで始めて、それから検証損失上の戻しが減衰し始めるまで層のサイズを増やしたり新しい層を追加し始めます。これを映画レビュー分類ネットワークで試してみましょう。

ベースラインとして Dense 層だけを使用する単純なモデルを作成します、それからより小さいものと大きいバージョンを作成し、それらを比較してみます。

 

ベースライン・モデルを作成する

baseline_model = keras.Sequential([
    # `input_shape` is only required here so that `.summary` works.
    keras.layers.Dense(16, activation='relu', input_shape=(NUM_WORDS,)),
    keras.layers.Dense(16, activation='relu'),
    keras.layers.Dense(1, activation='sigmoid')
])

baseline_model.compile(optimizer='adam',
                       loss='binary_crossentropy',
                       metrics=['accuracy', 'binary_crossentropy'])

baseline_model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense (Dense)                (None, 16)                160016    
_________________________________________________________________
dense_1 (Dense)              (None, 16)                272       
_________________________________________________________________
dense_2 (Dense)              (None, 1)                 17        
=================================================================
Total params: 160,305
Trainable params: 160,305
Non-trainable params: 0
_________________________________________________________________
baseline_history = baseline_model.fit(train_data,
                                      train_labels,
                                      epochs=20,
                                      batch_size=512,
                                      validation_data=(test_data, test_labels),
                                      verbose=2)
WARNING: Logging before flag parsing goes to stderr.
W0614 17:36:34.386701 139673406682880 deprecation.py:323] From /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/ops/math_grad.py:1250: add_dispatch_support.<locals>.wrapper (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.where in 2.0, which has the same broadcast rule as np.where

Train on 25000 samples, validate on 25000 samples
Epoch 1/20
25000/25000 - 5s - loss: 0.5435 - accuracy: 0.7573 - binary_crossentropy: 0.5435 - val_loss: 0.3906 - val_accuracy: 0.8675 - val_binary_crossentropy: 0.3906
Epoch 2/20
25000/25000 - 4s - loss: 0.2790 - accuracy: 0.9034 - binary_crossentropy: 0.2790 - val_loss: 0.2889 - val_accuracy: 0.8872 - val_binary_crossentropy: 0.2889
Epoch 3/20
25000/25000 - 4s - loss: 0.1947 - accuracy: 0.9311 - binary_crossentropy: 0.1947 - val_loss: 0.2861 - val_accuracy: 0.8861 - val_binary_crossentropy: 0.2861
Epoch 4/20
25000/25000 - 4s - loss: 0.1572 - accuracy: 0.9461 - binary_crossentropy: 0.1572 - val_loss: 0.3030 - val_accuracy: 0.8798 - val_binary_crossentropy: 0.3030
Epoch 5/20
25000/25000 - 4s - loss: 0.1297 - accuracy: 0.9570 - binary_crossentropy: 0.1297 - val_loss: 0.3207 - val_accuracy: 0.8769 - val_binary_crossentropy: 0.3207
Epoch 6/20
25000/25000 - 3s - loss: 0.1091 - accuracy: 0.9660 - binary_crossentropy: 0.1091 - val_loss: 0.3461 - val_accuracy: 0.8739 - val_binary_crossentropy: 0.3461
Epoch 7/20
25000/25000 - 4s - loss: 0.0923 - accuracy: 0.9724 - binary_crossentropy: 0.0923 - val_loss: 0.3743 - val_accuracy: 0.8699 - val_binary_crossentropy: 0.3743
Epoch 8/20
25000/25000 - 3s - loss: 0.0791 - accuracy: 0.9773 - binary_crossentropy: 0.0791 - val_loss: 0.4051 - val_accuracy: 0.8671 - val_binary_crossentropy: 0.4051
Epoch 9/20
25000/25000 - 4s - loss: 0.0656 - accuracy: 0.9833 - binary_crossentropy: 0.0656 - val_loss: 0.4379 - val_accuracy: 0.8652 - val_binary_crossentropy: 0.4379
Epoch 10/20
25000/25000 - 4s - loss: 0.0557 - accuracy: 0.9866 - binary_crossentropy: 0.0557 - val_loss: 0.4746 - val_accuracy: 0.8624 - val_binary_crossentropy: 0.4746
Epoch 11/20
25000/25000 - 4s - loss: 0.0435 - accuracy: 0.9910 - binary_crossentropy: 0.0435 - val_loss: 0.5135 - val_accuracy: 0.8601 - val_binary_crossentropy: 0.5135
Epoch 12/20
25000/25000 - 3s - loss: 0.0333 - accuracy: 0.9940 - binary_crossentropy: 0.0333 - val_loss: 0.5561 - val_accuracy: 0.8591 - val_binary_crossentropy: 0.5561
Epoch 13/20
25000/25000 - 4s - loss: 0.0250 - accuracy: 0.9965 - binary_crossentropy: 0.0250 - val_loss: 0.5952 - val_accuracy: 0.8569 - val_binary_crossentropy: 0.5952
Epoch 14/20
25000/25000 - 3s - loss: 0.0180 - accuracy: 0.9978 - binary_crossentropy: 0.0180 - val_loss: 0.6357 - val_accuracy: 0.8549 - val_binary_crossentropy: 0.6357
Epoch 15/20
25000/25000 - 4s - loss: 0.0133 - accuracy: 0.9988 - binary_crossentropy: 0.0133 - val_loss: 0.6691 - val_accuracy: 0.8562 - val_binary_crossentropy: 0.6691
Epoch 16/20
25000/25000 - 3s - loss: 0.0099 - accuracy: 0.9994 - binary_crossentropy: 0.0099 - val_loss: 0.7028 - val_accuracy: 0.8535 - val_binary_crossentropy: 0.7028
Epoch 17/20
25000/25000 - 4s - loss: 0.0073 - accuracy: 0.9997 - binary_crossentropy: 0.0073 - val_loss: 0.7265 - val_accuracy: 0.8543 - val_binary_crossentropy: 0.7265
Epoch 18/20
25000/25000 - 4s - loss: 0.0055 - accuracy: 0.9998 - binary_crossentropy: 0.0055 - val_loss: 0.7547 - val_accuracy: 0.8536 - val_binary_crossentropy: 0.7547
Epoch 19/20
25000/25000 - 4s - loss: 0.0042 - accuracy: 0.9998 - binary_crossentropy: 0.0042 - val_loss: 0.7752 - val_accuracy: 0.8539 - val_binary_crossentropy: 0.7752
Epoch 20/20
25000/25000 - 3s - loss: 0.0033 - accuracy: 0.9999 - binary_crossentropy: 0.0033 - val_loss: 0.7939 - val_accuracy: 0.8542 - val_binary_crossentropy: 0.7939

 

より小さいモデルを作成する

作成したばかりのベースライン・モデルに対して比較するためにより少ない隠れユニットを持つモデルを作成しましょう :

smaller_model = keras.Sequential([
    keras.layers.Dense(4, activation='relu', input_shape=(NUM_WORDS,)),
    keras.layers.Dense(4, activation='relu'),
    keras.layers.Dense(1, activation='sigmoid')
])

smaller_model.compile(optimizer='adam',
                      loss='binary_crossentropy',
                      metrics=['accuracy', 'binary_crossentropy'])

smaller_model.summary()
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_3 (Dense)              (None, 4)                 40004     
_________________________________________________________________
dense_4 (Dense)              (None, 4)                 20        
_________________________________________________________________
dense_5 (Dense)              (None, 1)                 5         
=================================================================
Total params: 40,029
Trainable params: 40,029
Non-trainable params: 0
_________________________________________________________________

そして同じデータを使用してモデルを訓練します :

smaller_history = smaller_model.fit(train_data,
                                    train_labels,
                                    epochs=20,
                                    batch_size=512,
                                    validation_data=(test_data, test_labels),
                                    verbose=2)
Train on 25000 samples, validate on 25000 samples
Epoch 1/20
25000/25000 - 4s - loss: 0.5741 - accuracy: 0.7644 - binary_crossentropy: 0.5741 - val_loss: 0.4683 - val_accuracy: 0.8536 - val_binary_crossentropy: 0.4683
Epoch 2/20
25000/25000 - 4s - loss: 0.3749 - accuracy: 0.8862 - binary_crossentropy: 0.3749 - val_loss: 0.3575 - val_accuracy: 0.8753 - val_binary_crossentropy: 0.3575
Epoch 3/20
25000/25000 - 4s - loss: 0.2826 - accuracy: 0.9096 - binary_crossentropy: 0.2826 - val_loss: 0.3133 - val_accuracy: 0.8838 - val_binary_crossentropy: 0.3133
Epoch 4/20
25000/25000 - 3s - loss: 0.2330 - accuracy: 0.9237 - binary_crossentropy: 0.2330 - val_loss: 0.2931 - val_accuracy: 0.8872 - val_binary_crossentropy: 0.2931
Epoch 5/20
25000/25000 - 4s - loss: 0.2007 - accuracy: 0.9344 - binary_crossentropy: 0.2007 - val_loss: 0.2855 - val_accuracy: 0.8878 - val_binary_crossentropy: 0.2855
Epoch 6/20
25000/25000 - 3s - loss: 0.1780 - accuracy: 0.9420 - binary_crossentropy: 0.1780 - val_loss: 0.2842 - val_accuracy: 0.8873 - val_binary_crossentropy: 0.2842
Epoch 7/20
25000/25000 - 4s - loss: 0.1594 - accuracy: 0.9486 - binary_crossentropy: 0.1594 - val_loss: 0.2882 - val_accuracy: 0.8856 - val_binary_crossentropy: 0.2882
Epoch 8/20
25000/25000 - 4s - loss: 0.1444 - accuracy: 0.9541 - binary_crossentropy: 0.1444 - val_loss: 0.2947 - val_accuracy: 0.8839 - val_binary_crossentropy: 0.2947
Epoch 9/20
25000/25000 - 4s - loss: 0.1317 - accuracy: 0.9586 - binary_crossentropy: 0.1317 - val_loss: 0.3093 - val_accuracy: 0.8786 - val_binary_crossentropy: 0.3093
Epoch 10/20
25000/25000 - 3s - loss: 0.1212 - accuracy: 0.9626 - binary_crossentropy: 0.1212 - val_loss: 0.3155 - val_accuracy: 0.8781 - val_binary_crossentropy: 0.3155
Epoch 11/20
25000/25000 - 4s - loss: 0.1111 - accuracy: 0.9669 - binary_crossentropy: 0.1111 - val_loss: 0.3260 - val_accuracy: 0.8763 - val_binary_crossentropy: 0.3260
Epoch 12/20
25000/25000 - 4s - loss: 0.1025 - accuracy: 0.9699 - binary_crossentropy: 0.1025 - val_loss: 0.3407 - val_accuracy: 0.8740 - val_binary_crossentropy: 0.3407
Epoch 13/20
25000/25000 - 4s - loss: 0.0944 - accuracy: 0.9736 - binary_crossentropy: 0.0944 - val_loss: 0.3518 - val_accuracy: 0.8733 - val_binary_crossentropy: 0.3518
Epoch 14/20
25000/25000 - 3s - loss: 0.0873 - accuracy: 0.9755 - binary_crossentropy: 0.0873 - val_loss: 0.3662 - val_accuracy: 0.8708 - val_binary_crossentropy: 0.3662
Epoch 15/20
25000/25000 - 4s - loss: 0.0809 - accuracy: 0.9783 - binary_crossentropy: 0.0809 - val_loss: 0.3801 - val_accuracy: 0.8690 - val_binary_crossentropy: 0.3801
Epoch 16/20
25000/25000 - 3s - loss: 0.0753 - accuracy: 0.9806 - binary_crossentropy: 0.0753 - val_loss: 0.3954 - val_accuracy: 0.8673 - val_binary_crossentropy: 0.3954
Epoch 17/20
25000/25000 - 4s - loss: 0.0685 - accuracy: 0.9835 - binary_crossentropy: 0.0685 - val_loss: 0.4096 - val_accuracy: 0.8662 - val_binary_crossentropy: 0.4096
Epoch 18/20
25000/25000 - 3s - loss: 0.0632 - accuracy: 0.9862 - binary_crossentropy: 0.0632 - val_loss: 0.4284 - val_accuracy: 0.8642 - val_binary_crossentropy: 0.4284
Epoch 19/20
25000/25000 - 4s - loss: 0.0584 - accuracy: 0.9874 - binary_crossentropy: 0.0584 - val_loss: 0.4417 - val_accuracy: 0.8636 - val_binary_crossentropy: 0.4417
Epoch 20/20
25000/25000 - 3s - loss: 0.0538 - accuracy: 0.9894 - binary_crossentropy: 0.0538 - val_loss: 0.4590 - val_accuracy: 0.8624 - val_binary_crossentropy: 0.4590

 

より大きいモデルを作成する

エクササイズとして、より大きいモデルさえも作成して、それがどのように素早く overfitting し始めるかを見ることができます。次に、このベンチマークに、問題が必要とするよりも遥かにより大きい capacity を持つネットワークを追加しましょう :

bigger_model = keras.models.Sequential([
    keras.layers.Dense(512, activation='relu', input_shape=(NUM_WORDS,)),
    keras.layers.Dense(512, activation='relu'),
    keras.layers.Dense(1, activation='sigmoid')
])

bigger_model.compile(optimizer='adam',
                     loss='binary_crossentropy',
                     metrics=['accuracy','binary_crossentropy'])

bigger_model.summary()
Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_6 (Dense)              (None, 512)               5120512   
_________________________________________________________________
dense_7 (Dense)              (None, 512)               262656    
_________________________________________________________________
dense_8 (Dense)              (None, 1)                 513       
=================================================================
Total params: 5,383,681
Trainable params: 5,383,681
Non-trainable params: 0
_________________________________________________________________

そして、再度、同じデータを使用してモデルを訓練します :

bigger_history = bigger_model.fit(train_data, train_labels,
                                  epochs=20,
                                  batch_size=512,
                                  validation_data=(test_data, test_labels),
                                  verbose=2)
Train on 25000 samples, validate on 25000 samples
Epoch 1/20
25000/25000 - 7s - loss: 0.3504 - accuracy: 0.8484 - binary_crossentropy: 0.3504 - val_loss: 0.3066 - val_accuracy: 0.8734 - val_binary_crossentropy: 0.3066
Epoch 2/20
25000/25000 - 7s - loss: 0.1451 - accuracy: 0.9473 - binary_crossentropy: 0.1451 - val_loss: 0.3286 - val_accuracy: 0.8747 - val_binary_crossentropy: 0.3286
Epoch 3/20
25000/25000 - 7s - loss: 0.0478 - accuracy: 0.9859 - binary_crossentropy: 0.0478 - val_loss: 0.4457 - val_accuracy: 0.8685 - val_binary_crossentropy: 0.4457
Epoch 4/20
25000/25000 - 7s - loss: 0.0072 - accuracy: 0.9991 - binary_crossentropy: 0.0072 - val_loss: 0.5815 - val_accuracy: 0.8684 - val_binary_crossentropy: 0.5815
Epoch 5/20
25000/25000 - 7s - loss: 8.4594e-04 - accuracy: 1.0000 - binary_crossentropy: 8.4594e-04 - val_loss: 0.6755 - val_accuracy: 0.8712 - val_binary_crossentropy: 0.6755
Epoch 6/20
25000/25000 - 7s - loss: 2.2323e-04 - accuracy: 1.0000 - binary_crossentropy: 2.2323e-04 - val_loss: 0.7145 - val_accuracy: 0.8712 - val_binary_crossentropy: 0.7145
Epoch 7/20
25000/25000 - 7s - loss: 1.3682e-04 - accuracy: 1.0000 - binary_crossentropy: 1.3682e-04 - val_loss: 0.7393 - val_accuracy: 0.8713 - val_binary_crossentropy: 0.7393
Epoch 8/20
25000/25000 - 7s - loss: 9.8825e-05 - accuracy: 1.0000 - binary_crossentropy: 9.8825e-05 - val_loss: 0.7579 - val_accuracy: 0.8710 - val_binary_crossentropy: 0.7579
Epoch 9/20
25000/25000 - 7s - loss: 7.5939e-05 - accuracy: 1.0000 - binary_crossentropy: 7.5939e-05 - val_loss: 0.7727 - val_accuracy: 0.8711 - val_binary_crossentropy: 0.7727
Epoch 10/20
25000/25000 - 7s - loss: 6.0573e-05 - accuracy: 1.0000 - binary_crossentropy: 6.0573e-05 - val_loss: 0.7846 - val_accuracy: 0.8720 - val_binary_crossentropy: 0.7846
Epoch 11/20
25000/25000 - 7s - loss: 4.9462e-05 - accuracy: 1.0000 - binary_crossentropy: 4.9462e-05 - val_loss: 0.7961 - val_accuracy: 0.8712 - val_binary_crossentropy: 0.7961
Epoch 12/20
25000/25000 - 7s - loss: 4.1340e-05 - accuracy: 1.0000 - binary_crossentropy: 4.1340e-05 - val_loss: 0.8059 - val_accuracy: 0.8716 - val_binary_crossentropy: 0.8059
Epoch 13/20
25000/25000 - 7s - loss: 3.4927e-05 - accuracy: 1.0000 - binary_crossentropy: 3.4927e-05 - val_loss: 0.8144 - val_accuracy: 0.8719 - val_binary_crossentropy: 0.8144
Epoch 14/20
25000/25000 - 7s - loss: 3.0028e-05 - accuracy: 1.0000 - binary_crossentropy: 3.0028e-05 - val_loss: 0.8225 - val_accuracy: 0.8717 - val_binary_crossentropy: 0.8225
Epoch 15/20
25000/25000 - 7s - loss: 2.6054e-05 - accuracy: 1.0000 - binary_crossentropy: 2.6054e-05 - val_loss: 0.8295 - val_accuracy: 0.8717 - val_binary_crossentropy: 0.8295
Epoch 16/20
25000/25000 - 7s - loss: 2.2880e-05 - accuracy: 1.0000 - binary_crossentropy: 2.2880e-05 - val_loss: 0.8366 - val_accuracy: 0.8718 - val_binary_crossentropy: 0.8366
Epoch 17/20
25000/25000 - 7s - loss: 2.0204e-05 - accuracy: 1.0000 - binary_crossentropy: 2.0204e-05 - val_loss: 0.8429 - val_accuracy: 0.8719 - val_binary_crossentropy: 0.8429
Epoch 18/20
25000/25000 - 7s - loss: 1.7972e-05 - accuracy: 1.0000 - binary_crossentropy: 1.7972e-05 - val_loss: 0.8487 - val_accuracy: 0.8718 - val_binary_crossentropy: 0.8487
Epoch 19/20
25000/25000 - 7s - loss: 1.6091e-05 - accuracy: 1.0000 - binary_crossentropy: 1.6091e-05 - val_loss: 0.8541 - val_accuracy: 0.8719 - val_binary_crossentropy: 0.8541
Epoch 20/20
25000/25000 - 7s - loss: 1.4484e-05 - accuracy: 1.0000 - binary_crossentropy: 1.4484e-05 - val_loss: 0.8593 - val_accuracy: 0.8720 - val_binary_crossentropy: 0.8593

 

訓練と検証損失をプロットする

実線は訓練損失を示し、破線は検証損失を表示します (remember: より低い検証損失はより良いモデルを示します)。ここで、より小さいネットワークはベースラインモデルよりも後で overfitting し始めて (4 ではなくて 6 エポック後) そしてそのパフォーマンスはひとたび overfitting し始めれば遥かによりゆっくりと低下します。

def plot_history(histories, key='binary_crossentropy'):
  plt.figure(figsize=(16,10))

  for name, history in histories:
    val = plt.plot(history.epoch, history.history['val_'+key],
                   '--', label=name.title()+' Val')
    plt.plot(history.epoch, history.history[key], color=val[0].get_color(),
             label=name.title()+' Train')

  plt.xlabel('Epochs')
  plt.ylabel(key.replace('_',' ').title())
  plt.legend()

  plt.xlim([0,max(history.epoch)])


plot_history([('baseline', baseline_history),
              ('smaller', smaller_history),
              ('bigger', bigger_history)])

より大きいネットワークは殆ど直ちに、丁度 1 エポック後に overfitting し始めます、そして遥かにより厳しく overfit することが分かるでしょう。ネットワークがより多くの capacity を持つほど、より迅速に訓練データをモデリングすることが可能です (低い訓練損失という結果になります) が、それはより overfitting しやすいです (訓練と検証損失間の巨大な差異という結果になります) 。

 

overfitting を回避するためのストラテジー

重み正則化を追加する

貴方はオッカムの剃刀の原理を知っているかもしれません: 何かについて 2 つの説明が与えられたとき、最も正しい可能性がある説明は「単純な」もの、最小量の仮定をするものです。これはまたニューラルネットワークにより学習されたモデルにも適用されます : ある訓練データとネットワーク・アーキテクチャが与えられたとき、データを説明できる重み値の複数のセット (複数のモデル) があり、そしてより単純なモデルは複雑なものよりも overfit する可能性が低いです。

このコンテキストでの「単純なモデル」はそこではパラメータ値の分布がより少ないエントロピーを持ちます (あるいはまとめてより少ないパラメータを持つモデルです、上のセクションで見たように)。そのため overfitting を軽減する一般的な方法はその (ネットワークの) 重みを小さい値だけを取るように強制してネットワークの複雑さに制約を置くことです、それは重み値の分布をより「正則 (= regular)」にします。これは「重み正則化」と呼ばれ、それはネットワークの損失関数に巨大な重みを持つことに関するコストを追加することによって成されます。このコストは 2 つのフレーバーに分けられます :

  • L1 正則化, そこでは追加されるコストは重み係数の絶対値に (i.e. 重みの「L1 ノルム」と呼称されるものに) 比例します。
  • L2 正則化, そこでは追加されるコストは重み係数の値の二乗に (i.e. 重みの「L2 ノルム」と呼称されるものに) 比例します。L2 正則化はまたニューラルネットワークのコンテキストでは重み減衰とも呼称されます。異なる名前に混乱してはいけません。重み減衰は数学的には L2 正則化と正確に同じです。

L1 正則化は重みパラメータの幾つかをゼロにするために疎性を導入します。L2 正則化は重みパラメータにそれらを疎にすることなくペナルティを課します – L2 がより一般的である一つの理由です。

tf.keras では、重み正則化は層にキーワード引数として重み regularizer インスタンスを渡すことにより追加されます。今は L2 重み正則化を追加しましょう。

l2_model = keras.models.Sequential([
    keras.layers.Dense(16, kernel_regularizer=keras.regularizers.l2(0.001),
                       activation='relu', input_shape=(NUM_WORDS,)),
    keras.layers.Dense(16, kernel_regularizer=keras.regularizers.l2(0.001),
                       activation='relu'),
    keras.layers.Dense(1, activation='sigmoid')
])

l2_model.compile(optimizer='adam',
                 loss='binary_crossentropy',
                 metrics=['accuracy', 'binary_crossentropy'])

l2_model_history = l2_model.fit(train_data, train_labels,
                                epochs=20,
                                batch_size=512,
                                validation_data=(test_data, test_labels),
                                verbose=2)
Train on 25000 samples, validate on 25000 samples
Epoch 1/20
25000/25000 - 5s - loss: 0.5295 - accuracy: 0.8148 - binary_crossentropy: 0.4887 - val_loss: 0.3801 - val_accuracy: 0.8801 - val_binary_crossentropy: 0.3360
Epoch 2/20
25000/25000 - 4s - loss: 0.3049 - accuracy: 0.9076 - binary_crossentropy: 0.2554 - val_loss: 0.3387 - val_accuracy: 0.8876 - val_binary_crossentropy: 0.2855
Epoch 3/20
25000/25000 - 4s - loss: 0.2536 - accuracy: 0.9302 - binary_crossentropy: 0.1978 - val_loss: 0.3437 - val_accuracy: 0.8851 - val_binary_crossentropy: 0.2863
Epoch 4/20
25000/25000 - 4s - loss: 0.2330 - accuracy: 0.9393 - binary_crossentropy: 0.1738 - val_loss: 0.3631 - val_accuracy: 0.8781 - val_binary_crossentropy: 0.3027
Epoch 5/20
25000/25000 - 4s - loss: 0.2176 - accuracy: 0.9470 - binary_crossentropy: 0.1562 - val_loss: 0.3712 - val_accuracy: 0.8769 - val_binary_crossentropy: 0.3091
Epoch 6/20
25000/25000 - 4s - loss: 0.2059 - accuracy: 0.9526 - binary_crossentropy: 0.1428 - val_loss: 0.3836 - val_accuracy: 0.8749 - val_binary_crossentropy: 0.3201
Epoch 7/20
25000/25000 - 4s - loss: 0.1990 - accuracy: 0.9542 - binary_crossentropy: 0.1348 - val_loss: 0.4034 - val_accuracy: 0.8713 - val_binary_crossentropy: 0.3387
Epoch 8/20
25000/25000 - 4s - loss: 0.1929 - accuracy: 0.9567 - binary_crossentropy: 0.1276 - val_loss: 0.4146 - val_accuracy: 0.8696 - val_binary_crossentropy: 0.3490
Epoch 9/20
25000/25000 - 5s - loss: 0.1878 - accuracy: 0.9585 - binary_crossentropy: 0.1214 - val_loss: 0.4298 - val_accuracy: 0.8656 - val_binary_crossentropy: 0.3630
Epoch 10/20
25000/25000 - 5s - loss: 0.1829 - accuracy: 0.9607 - binary_crossentropy: 0.1157 - val_loss: 0.4405 - val_accuracy: 0.8670 - val_binary_crossentropy: 0.3730
Epoch 11/20
25000/25000 - 4s - loss: 0.1779 - accuracy: 0.9635 - binary_crossentropy: 0.1101 - val_loss: 0.4512 - val_accuracy: 0.8653 - val_binary_crossentropy: 0.3830
Epoch 12/20
25000/25000 - 5s - loss: 0.1747 - accuracy: 0.9628 - binary_crossentropy: 0.1060 - val_loss: 0.4647 - val_accuracy: 0.8634 - val_binary_crossentropy: 0.3958
Epoch 13/20
25000/25000 - 5s - loss: 0.1703 - accuracy: 0.9672 - binary_crossentropy: 0.1012 - val_loss: 0.4761 - val_accuracy: 0.8625 - val_binary_crossentropy: 0.4067
Epoch 14/20
25000/25000 - 4s - loss: 0.1683 - accuracy: 0.9660 - binary_crossentropy: 0.0986 - val_loss: 0.4919 - val_accuracy: 0.8589 - val_binary_crossentropy: 0.4218
Epoch 15/20
25000/25000 - 4s - loss: 0.1678 - accuracy: 0.9674 - binary_crossentropy: 0.0976 - val_loss: 0.5063 - val_accuracy: 0.8605 - val_binary_crossentropy: 0.4356
Epoch 16/20
25000/25000 - 4s - loss: 0.1642 - accuracy: 0.9682 - binary_crossentropy: 0.0928 - val_loss: 0.5094 - val_accuracy: 0.8572 - val_binary_crossentropy: 0.4380
Epoch 17/20
25000/25000 - 4s - loss: 0.1613 - accuracy: 0.9689 - binary_crossentropy: 0.0901 - val_loss: 0.5202 - val_accuracy: 0.8590 - val_binary_crossentropy: 0.4488
Epoch 18/20
25000/25000 - 4s - loss: 0.1623 - accuracy: 0.9689 - binary_crossentropy: 0.0902 - val_loss: 0.5563 - val_accuracy: 0.8546 - val_binary_crossentropy: 0.4838
Epoch 19/20
25000/25000 - 4s - loss: 0.1610 - accuracy: 0.9682 - binary_crossentropy: 0.0883 - val_loss: 0.5347 - val_accuracy: 0.8559 - val_binary_crossentropy: 0.4617
Epoch 20/20
25000/25000 - 5s - loss: 0.1520 - accuracy: 0.9752 - binary_crossentropy: 0.0793 - val_loss: 0.5456 - val_accuracy: 0.8568 - val_binary_crossentropy: 0.4734

l2(0.001) は層の重み行列の総ての係数が 0.001 * weight_coefficient_value**2 をネットワークの総計損失に追加することを意味します。このペナルティは訓練時のみに追加されますので、このネットワークに対する損失はテスト時よりも訓練時に遥かに高くなることに注意してください。

L2 正則化ペナルティのインパクトがここにあります :

plot_history([('baseline', baseline_history),
              ('l2', l2_model_history)])

見て取れるように、両者のモデルが同じパラメータ数を持つ場合でさえも、L2 正則化されたモデルはベースライン・モデルよりも overfitting に大して遥かにより耐性があります。

 

dropout を追加する

Dropout は最も効果的で最も一般に使用されるニューラルネットワークのための正則化テクニックの一つで、University of Toronto の Hinton とその学生により開発されました。Dropout は層に適用され、訓練の間層の幾つかの出力特徴をランダムに “dropping out” (i.e. ゼロに設定) することから成ります。与えられた層は訓練の間に与えられた入力サンプルに対して通常はベクトル [0.2, 0.5, 1.3, 0.8, 1.1] を返したと仮定します ; dropout を適用した後、このベクトルはランダムに分布する幾つかのゼロ要素を持つでしょう、e.g. [0, 0.5, 1.3, 0, 1.1] です。”dropout 率” はゼロにされる特徴の割合です ; それは通常は 0.2 と 0.5 の間に設定されます。テスト時には、ユニットは drop out されず、代わりに層の出力値は dropout 率に等しい倍数でスケールダウンされます、これは訓練時よりもより多いユニットが有効であるという事実のためにバランスを取るためです。

tf.keras では Dropout 層を通してネットワークに dropout を導入できます、それはすぐ前の層の出力に適用されます。

IMDB ネットワークに 2 つの Dropout 層をそれらがどのように上手く overfitting を減じるかを見るために追加しましょう :

dpt_model = keras.models.Sequential([
    keras.layers.Dense(16, activation='relu', input_shape=(NUM_WORDS,)),
    keras.layers.Dropout(0.5),
    keras.layers.Dense(16, activation='relu'),
    keras.layers.Dropout(0.5),
    keras.layers.Dense(1, activation='sigmoid')
])

dpt_model.compile(optimizer='adam',
                  loss='binary_crossentropy',
                  metrics=['accuracy','binary_crossentropy'])

dpt_model_history = dpt_model.fit(train_data, train_labels,
                                  epochs=20,
                                  batch_size=512,
                                  validation_data=(test_data, test_labels),
                                  verbose=2)
Train on 25000 samples, validate on 25000 samples
Epoch 1/20
25000/25000 - 5s - loss: 0.6143 - accuracy: 0.6682 - binary_crossentropy: 0.6143 - val_loss: 0.4819 - val_accuracy: 0.8586 - val_binary_crossentropy: 0.4819
Epoch 2/20
25000/25000 - 4s - loss: 0.4486 - accuracy: 0.8210 - binary_crossentropy: 0.4486 - val_loss: 0.3502 - val_accuracy: 0.8823 - val_binary_crossentropy: 0.3502
Epoch 3/20
25000/25000 - 4s - loss: 0.3543 - accuracy: 0.8742 - binary_crossentropy: 0.3543 - val_loss: 0.3037 - val_accuracy: 0.8895 - val_binary_crossentropy: 0.3037
Epoch 4/20
25000/25000 - 4s - loss: 0.2968 - accuracy: 0.9008 - binary_crossentropy: 0.2968 - val_loss: 0.2826 - val_accuracy: 0.8897 - val_binary_crossentropy: 0.2826
Epoch 5/20
25000/25000 - 4s - loss: 0.2510 - accuracy: 0.9173 - binary_crossentropy: 0.2510 - val_loss: 0.2767 - val_accuracy: 0.8884 - val_binary_crossentropy: 0.2767
Epoch 6/20
25000/25000 - 4s - loss: 0.2200 - accuracy: 0.9300 - binary_crossentropy: 0.2200 - val_loss: 0.2909 - val_accuracy: 0.8857 - val_binary_crossentropy: 0.2909
Epoch 7/20
25000/25000 - 4s - loss: 0.1907 - accuracy: 0.9376 - binary_crossentropy: 0.1907 - val_loss: 0.2935 - val_accuracy: 0.8847 - val_binary_crossentropy: 0.2935
Epoch 8/20
25000/25000 - 4s - loss: 0.1705 - accuracy: 0.9476 - binary_crossentropy: 0.1705 - val_loss: 0.3140 - val_accuracy: 0.8850 - val_binary_crossentropy: 0.3140
Epoch 9/20
25000/25000 - 4s - loss: 0.1468 - accuracy: 0.9536 - binary_crossentropy: 0.1468 - val_loss: 0.3418 - val_accuracy: 0.8811 - val_binary_crossentropy: 0.3418
Epoch 10/20
25000/25000 - 5s - loss: 0.1313 - accuracy: 0.9598 - binary_crossentropy: 0.1313 - val_loss: 0.3731 - val_accuracy: 0.8803 - val_binary_crossentropy: 0.3731
Epoch 11/20
25000/25000 - 4s - loss: 0.1218 - accuracy: 0.9629 - binary_crossentropy: 0.1218 - val_loss: 0.3755 - val_accuracy: 0.8796 - val_binary_crossentropy: 0.3755
Epoch 12/20
25000/25000 - 4s - loss: 0.1102 - accuracy: 0.9656 - binary_crossentropy: 0.1102 - val_loss: 0.3962 - val_accuracy: 0.8792 - val_binary_crossentropy: 0.3962
Epoch 13/20
25000/25000 - 4s - loss: 0.1022 - accuracy: 0.9681 - binary_crossentropy: 0.1022 - val_loss: 0.4369 - val_accuracy: 0.8790 - val_binary_crossentropy: 0.4369
Epoch 14/20
25000/25000 - 5s - loss: 0.0931 - accuracy: 0.9702 - binary_crossentropy: 0.0931 - val_loss: 0.4528 - val_accuracy: 0.8778 - val_binary_crossentropy: 0.4528
Epoch 15/20
25000/25000 - 4s - loss: 0.0886 - accuracy: 0.9726 - binary_crossentropy: 0.0886 - val_loss: 0.4817 - val_accuracy: 0.8754 - val_binary_crossentropy: 0.4817
Epoch 16/20
25000/25000 - 4s - loss: 0.0808 - accuracy: 0.9745 - binary_crossentropy: 0.0808 - val_loss: 0.4971 - val_accuracy: 0.8745 - val_binary_crossentropy: 0.4971
Epoch 17/20
25000/25000 - 4s - loss: 0.0758 - accuracy: 0.9745 - binary_crossentropy: 0.0758 - val_loss: 0.4992 - val_accuracy: 0.8746 - val_binary_crossentropy: 0.4992
Epoch 18/20
25000/25000 - 4s - loss: 0.0720 - accuracy: 0.9762 - binary_crossentropy: 0.0720 - val_loss: 0.5373 - val_accuracy: 0.8756 - val_binary_crossentropy: 0.5373
Epoch 19/20
25000/25000 - 4s - loss: 0.0709 - accuracy: 0.9768 - binary_crossentropy: 0.0709 - val_loss: 0.5491 - val_accuracy: 0.8744 - val_binary_crossentropy: 0.5491
Epoch 20/20
25000/25000 - 4s - loss: 0.0663 - accuracy: 0.9783 - binary_crossentropy: 0.0663 - val_loss: 0.5506 - val_accuracy: 0.8740 - val_binary_crossentropy: 0.5506
plot_history([('baseline', baseline_history),
              ('dropout', dpt_model_history)])

dropout の追加はベースライン・モデルを超える明確な改良です。

復習のために: ここにニューラルネットワークで overfitting を回避するための最も一般的な方法があります :

  • より多くの訓練データを得る。
  • ネットワークの capcity を減じる。
  • 重み正則を追加する。
  • dropout を追加する。

そしてこのガイドでカバーされていない 2 つの重要なアプローチはデータ増強とバッチ正規化です。

 

以上



TensorFlow 2.0 Beta : Tutorials : ML 基本 :- 燃費効率 : 回帰

TensorFlow 2.0 Beta : Beginner Tutorials : ML 基本 :- 燃費効率 : 回帰 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 06/22/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Beta – Beginner Tutorials – ML basics の以下のページを翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

ML 基本 :- 燃費効率 : 回帰

回帰問題では、価格や確率のような、連続値の出力を予測することを狙いとします。これを分類問題と対比すると、そこではクラスのリストからクラスを選択することを目的とします (例えば、そこでは写真がりんごかオレンジを含む、どのフルーツが写真にあるかを認識します)。

この notebook は古典的な Auto MPG データセットを使用して 1970s 末と 1980s 初期の自動車の燃費効率を予測するモデルを構築します。これを行なうために、その期間からの多くの自動車の記述を持つモデルを提供します。この記述は次のような属性を含みます : 気筒 (= cylinders), 排気量 (= displacement), 馬力 (= horsepower) そして重量 (= weight)。

このサンプルは tf.kreas API を使用します、詳細は このガイド を見てください。

# Use seaborn for pairplot
!pip install -q seaborn
from __future__ import absolute_import, division, print_function, unicode_literals

import pathlib

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns

!pip install -q tensorflow==2.0.0-beta1
import tensorflow as tf

from tensorflow import keras
from tensorflow.keras import layers

print(tf.__version__)
2.0.0-beta1

 

The Auto MPG データセット

データセットは UCI Machine Learning レポジトリ から利用可能です。

 

データを得る

最初にデータセットをダウンロードします。

dataset_path = keras.utils.get_file("auto-mpg.data", "http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data")
dataset_path
Downloading data from http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data
32768/30286 [================================] - 0s 4us/step

'/home/kbuilder/.keras/datasets/auto-mpg.data'

pandas を使用してそれをインポートします。

column_names = ['MPG','Cylinders','Displacement','Horsepower','Weight',
                'Acceleration', 'Model Year', 'Origin']
raw_dataset = pd.read_csv(dataset_path, names=column_names,
                      na_values = "?", comment='\t',
                      sep=" ", skipinitialspace=True)

dataset = raw_dataset.copy()
dataset.tail()

MPG Cylinders Displacement Horsepower Weight Acceleration Model Year Origin
393 27.0 4 140.0 86.0 2790.0 15.6 82 1
394 44.0 4 97.0 52.0 2130.0 24.6 82 2
395 32.0 4 135.0 84.0 2295.0 11.6 82 1
396 28.0 4 120.0 79.0 2625.0 18.6 82 1
397 31.0 4 119.0 82.0 2720.0 19.4 82 1

 

データをクリーンにする

データセットは幾つかの未知の値を含みます。

dataset.isna().sum()
MPG             0
Cylinders       0
Displacement    0
Horsepower      6
Weight          0
Acceleration    0
Model Year      0
Origin          0
dtype: int64

この初期チュートリアルを単純に維持するためにこれらの行は破棄します。

dataset = dataset.dropna()

“Origin” カラムは実際には categorical であり、numeric ではありません。そこでそれを one-hot に変換します :

origin = dataset.pop('Origin')
dataset['USA'] = (origin == 1)*1.0
dataset['Europe'] = (origin == 2)*1.0
dataset['Japan'] = (origin == 3)*1.0
dataset.tail()

MPG Cylinders Displacement Horsepower Weight Acceleration Model Year USA Europe Japan
393 27.0 4 140.0 86.0 2790.0 15.6 82 1.0 0.0 0.0
394 44.0 4 97.0 52.0 2130.0 24.6 82 0.0 1.0 0.0
395 32.0 4 135.0 84.0 2295.0 11.6 82 1.0 0.0 0.0
396 28.0 4 120.0 79.0 2625.0 18.6 82 1.0 0.0 0.0
397 31.0 4 119.0 82.0 2720.0 19.4 82 1.0 0.0 0.0

 

データを訓練とテストに分割する

さてデータセットを訓練セットとテストセットに分割します。

モデルの最後の評価でテストセットを使用します。

train_dataset = dataset.sample(frac=0.8,random_state=0)
test_dataset = dataset.drop(train_dataset.index)

 

データを調査する

訓練セットからカラムの 2,3 のペアの同時分布を簡単に見てみます。

sns.pairplot(train_dataset[["MPG", "Cylinders", "Displacement", "Weight"]], diag_kind="kde")
<seaborn.axisgrid.PairGrid at 0x7fa0c95859e8>

全体的な統計情報も見てみます :

train_stats = train_dataset.describe()
train_stats.pop("MPG")
train_stats = train_stats.transpose()
train_stats

count mean std min 25% 50% 75% max
Cylinders 314.0 5.477707 1.699788 3.0 4.00 4.0 8.00 8.0
Displacement 314.0 195.318471 104.331589 68.0 105.50 151.0 265.75 455.0
Horsepower 314.0 104.869427 38.096214 46.0 76.25 94.5 128.00 225.0
Weight 314.0 2990.251592 843.898596 1649.0 2256.50 2822.5 3608.00 5140.0
Acceleration 314.0 15.559236 2.789230 8.0 13.80 15.5 17.20 24.8
Model Year 314.0 75.898089 3.675642 70.0 73.00 76.0 79.00 82.0
USA 314.0 0.624204 0.485101 0.0 0.00 1.0 1.00 1.0
Europe 314.0 0.178344 0.383413 0.0 0.00 0.0 0.00 1.0
Japan 314.0 0.197452 0.398712 0.0 0.00 0.0 0.00 1.0

 

ラベルから特徴を分ける

特徴からターゲット値、あるいは「ラベル」を分離します。このラベルがモデルを訓練して予測する値です。

train_labels = train_dataset.pop('MPG')
test_labels = test_dataset.pop('MPG')

 

データを正規化する

上の train_stats ブロックを再度見て各特徴の範囲がどのように異なっているかに注意してください。

異なるスケールと範囲を使用する特徴を正規化することは良い実践です。特徴正規化なしでもモデルは収束するかもしれませんが、それは訓練をより困難にし、そしてそれは結果としてのモデルを入力で使用される単位の選択に依拠させます。

Note: これらの統計情報を訓練データセットだけから意図的に生成しますが、こららの統計情報はまたテストデータセットを正規化するためにも使用されます。テストデータセットを (モデルがその上で訓練されたのと) 同じ分布に射影するためにそれを行なう必要があります。

def norm(x):
  return (x - train_stats['mean']) / train_stats['std']
normed_train_data = norm(train_dataset)
normed_test_data = norm(test_dataset)

この正規化されたデータはモデルを訓練するために使用するものです。

Caution: ここで入力を正規化するために使用された統計情報 (平均と標準偏差) は、先に行なった one-hot エンコーディングと一緒に、モデルに供給されるどのような他のデータにも適用される必要があります。それはテストセットそしてプロダクションでモデルが使用されるときのライブデータを含みます。

 

モデル

モデルを構築する

私達のモデルを構築しましょう。ここで、2 つの密に結合した隠れ層、そして単一の連続値を返す出力層を持つ Sequential モデルを使用します。モデル構築ステップは関数, build_model にラップされます、何故ならば後で 2 番目のモデルを作成するからです。

def build_model():
  model = keras.Sequential([
    layers.Dense(64, activation='relu', input_shape=[len(train_dataset.keys())]),
    layers.Dense(64, activation='relu'),
    layers.Dense(1)
  ])

  optimizer = tf.keras.optimizers.RMSprop(0.001)

  model.compile(loss='mse',
                optimizer=optimizer,
                metrics=['mae', 'mse'])
  return model
model = build_model()

 

モデルを調査する

モデルの単純な記述をプリントするために .summary メソッドを使用します。

model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense (Dense)                (None, 64)                640       
_________________________________________________________________
dense_1 (Dense)              (None, 64)                4160      
_________________________________________________________________
dense_2 (Dense)              (None, 1)                 65        
=================================================================
Total params: 4,865
Trainable params: 4,865
Non-trainable params: 0
_________________________________________________________________

さてモデルを試してみましょう。訓練データから 10 サンプルのバッチを取ってその上で model.predict を呼び出します。

example_batch = normed_train_data[:10]
example_result = model.predict(example_batch)
example_result
array([[ 0.01432403],
       [ 0.05611863],
       [-0.7658546 ],
       [-0.03145953],
       [-0.14918138],
       [-0.17592657],
       [-0.13245817],
       [-0.17190717],
       [-0.11158038],
       [ 0.18116453]], dtype=float32)

それは動作しているようです、そしてそれは期待される shape と type の結果を生成します。

 

モデルを訓練する

モデルを 1000 エポックの間訓練して訓練と検証精度を history オブジェクトに記録します。

# Display training progress by printing a single dot for each completed epoch
class PrintDot(keras.callbacks.Callback):
  def on_epoch_end(self, epoch, logs):
    if epoch % 100 == 0: print('')
    print('.', end='')

EPOCHS = 1000

history = model.fit(
  normed_train_data, train_labels,
  epochs=EPOCHS, validation_split = 0.2, verbose=0,
  callbacks=[PrintDot()])
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................

history オブジェクトにストアされている統計情報を使用してモデルの訓練進捗を可視化します。

hist = pd.DataFrame(history.history)
hist['epoch'] = history.epoch
hist.tail()

loss mae mse val_loss val_mae val_mse epoch
995 3.169360 1.137359 3.169360 8.839724 2.252794 8.839724 995
996 3.343072 1.140639 3.343073 8.830334 2.231025 8.830334 996
997 3.105232 1.128542 3.105232 9.016378 2.240795 9.016376 997
998 3.309495 1.193391 3.309495 8.855472 2.234031 8.855473 998
999 3.440103 1.183400 3.440103 8.744971 2.213535 8.744972 999

def plot_history(history):
  hist = pd.DataFrame(history.history)
  hist['epoch'] = history.epoch

  plt.figure()
  plt.xlabel('Epoch')
  plt.ylabel('Mean Abs Error [MPG]')
  plt.plot(hist['epoch'], hist['mae'],
           label='Train Error')
  plt.plot(hist['epoch'], hist['val_mae'],
           label = 'Val Error')
  plt.ylim([0,5])
  plt.legend()

  plt.figure()
  plt.xlabel('Epoch')
  plt.ylabel('Mean Square Error [$MPG^2$]')
  plt.plot(hist['epoch'], hist['mse'],
           label='Train Error')
  plt.plot(hist['epoch'], hist['val_mse'],
           label = 'Val Error')
  plt.ylim([0,20])
  plt.legend()
  plt.show()


plot_history(history)

このグラフはおよそ 100 エポック後には検証エラーについて殆ど改良しない、あるいは劣化さえ示します。検証スコアが改善しないとき訓練を自動的に停止するように model.fit 呼び出しを更新しましょう。EarlyStopping コールバックを使用します、これは総てのエポックのために訓練条件をテストします。エポックの設定された総数が改善を示すことなく経過する場合、訓練を自動的に停止します。

このコールバックについて ここで 更に学習できます。

model = build_model()

# The patience parameter is the amount of epochs to check for improvement
early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=10)

history = model.fit(normed_train_data, train_labels, epochs=EPOCHS,
                    validation_split = 0.2, verbose=0, callbacks=[early_stop, PrintDot()])

plot_history(history)

................................................

グラフは検証セット上、平均エラーが通常は +/- 2 MPG まわりであることを示しています。これは良いでしょうか?その決定は貴方に委ねましょう。

モデルを訓練するときに使用しなかった、テスト セットを使用してモデルがどの程度上手く一般化されたかを見てみましょう。これは、現実世界でモデルを使用するときどの程度上手く予測することをモデルに期待できるかを私達に教えてくれます。

loss, mae, mse = model.evaluate(normed_test_data, test_labels, verbose=0)

print("Testing set Mean Abs Error: {:5.2f} MPG".format(mae))
Testing set Mean Abs Error:  1.83 MPG

 

予測を行なう

最後に、テストセットのデータを使用してMPG 値を予測します :

test_predictions = model.predict(normed_test_data).flatten()

plt.scatter(test_labels, test_predictions)
plt.xlabel('True Values [MPG]')
plt.ylabel('Predictions [MPG]')
plt.axis('equal')
plt.axis('square')
plt.xlim([0,plt.xlim()[1]])
plt.ylim([0,plt.ylim()[1]])
_ = plt.plot([-100, 100], [-100, 100])

私達のモデルは適度に上手く予想しているようです。エラー分布を見てみましょう。

error = test_predictions - test_labels
plt.hist(error, bins = 25)
plt.xlabel("Prediction Error [MPG]")
_ = plt.ylabel("Count")

それは完全にはガウシアン (分布) ではありませんが、それを期待できます、何故ならばサンプルの数が非常に小さいからです。

 

最後に

この notebook は回帰問題を扱う幾つかのテクニックを紹介しました。

  • Mean Squared Error (MSE) は回帰問題のために使用される一般的な損失関数です (異なる損失関数が分類問題に使われます)。
  • 同様に、回帰に使用される評価メトリクスは分類とは異なります。一般的な回帰メトリクスは Mean Absolute Error (MAE) です。
  • 数値入力データ特徴が異なる範囲の値を持つとき、各特徴は独立的に同じ範囲にスケールされるべきです。
  • それほど多くの訓練データがない場合、overfitting を回避するために一つのテクニックは少ない隠れ層を持つ小さいネットワークを選択することです。
  • Early stopping は overfitting を回避するための有用なテクニックです。
 

以上



TensorFlow 2.0 Beta : Tutorials : ML 基本 :- 構造化データを分類する

TensorFlow 2.0 Beta : Beginner Tutorials : ML 基本 :- 構造化データを分類する (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 06/22/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Tutorials : – Beginner Tutorials – ML basics の以下のページを翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

ML 基本 :- 構造化データを分類する

このチュートリアルは構造化データ (e.g. CSV の表形式データ) をどのように分類するかを実演します。モデルを定義するために Keras を、そして CSV のカラムからモデルを訓練するために使用される特徴へマップするブリッジとして feature columns を使用します。このチュートリアルは以下を行なうための完全なコードを含みます :

  • Pandas を使用して CSV ファイルをロードする。
  • tf.data を使用して行をバッチ処理してシャッフルするために入力パイプラインを構築する。
  • feature columns を使用して CSV のカラムからモデルを訓練するために使用される特徴にマップする。
  • Keras を使用して、モデルを構築、訓練そして評価する。

 

データセット

Cleveland Clinic Foundation for Heart Disease により提供される小さい データセット を使用します。CSV には数百行あります。各行は患者を表し、各カラム (列) は属性を表します。患者が心臓疾患を持つか否かを予測するためにこの情報を使用します、これはこのデータセットにおける二値分類タスクです。

次はこのデータセットの 記述 です。numeric と categorical カラムの両者があることが分かるでしょう。

カラム 説明 特徴型 データ型
Age 年齢 Numerical integer
Sex (1 = male; 0 = female) Categorical integer
CP 胸の痛みのタイプ (0, 1, 2, 3, 4) Categorical integer
Trestbpd 安静時血圧 (in mm Hg 入院時) Numerical integer
Chol 血清コレステロール in mg/dl Numerical integer
FBS (空腹時血糖値 > 120 mg/dl) (1 = true; 0 = false) Categorical integer
RestECG 安静時心電図結果 (0, 1, 2) Categorical integer
Thalach 得られた最大心拍数 Numerical integer
Exang 労作性狭心症 (1 = yes; 0 = no) Categorical integer
Oldpeak ST 低下 induced by exercise relative to rest Numerical integer
Slope The slope of 最大運動時 ST 部分 Numerical float
CA 透視法により色付けられた主要管の数 (0-3) Numerical integer
Thal 3 = normal; 6 = fixed defect; 7 = reversible defect Categorical string
Target 心臓疾患の診断 (1 = true; 0 = false) Classification integer

 

TensorFlow と他のライブラリをインポートする

!pip install -q sklearn
from __future__ import absolute_import, division, print_function, unicode_literals

import numpy as np
import pandas as pd

!pip install -q tensorflow==2.0.0-beta1
import tensorflow as tf

from tensorflow import feature_column
from tensorflow.keras import layers
from sklearn.model_selection import train_test_split

 

dataframe を作成するために Pandas を使用する

Pandas はロードや構造化データで作業するための多くの役立つユティリティを持つ Python ライブラリです。URL からデータセットをダウンロードしてそれを dataframe にロードするために Pandas を使用します。

URL = 'https://storage.googleapis.com/applied-dl/heart.csv'
dataframe = pd.read_csv(URL)
dataframe.head()

age sex cp trestbps chol fbs restecg thalach exang oldpeak slope ca thal target
0 63 1 1 145 233 1 2 150 0 0.3 3 0 fixed 0
1 67 1 4 160 286 0 2 108 1 1.5 2 3 normal 1
2 67 1 4 120 229 0 2 129 1 2.6 2 2 reversible 0
3 37 1 3 130 250 0 0 187 0 3.5 3 0 normal 0
4 41 0 2 130 204 0 2 172 0 1.4 1 0 normal 0

 

dataframe を訓練、検証とテストに分割する

ダウンロードしたデータセットは単一の CSV ファイルでした。これを訓練、検証とテストセットに分割します。

train, test = train_test_split(dataframe, test_size=0.2)
train, val = train_test_split(train, test_size=0.2)
print(len(train), 'train examples')
print(len(val), 'validation examples')
print(len(test), 'test examples')
193 train examples
49 validation examples
61 test examples

 

tf.data を使用して入力パイプラインを作成する

次に、tf.data で dataframe をラップします。これは Pandas dataframe のカラムからモデルを訓練するために使用される features へマップするブリッジとして feature columns を使用することを可能にします。 (メモリに収まらないほどに) 非常に巨大な CSV ファイルで作業するとしても、それをディスクから直接読むために tf.data を使用するでしょう。それはこのチュートリアルではカバーされません。

# A utility method to create a tf.data dataset from a Pandas Dataframe
def df_to_dataset(dataframe, shuffle=True, batch_size=32):
  dataframe = dataframe.copy()
  labels = dataframe.pop('target')
  ds = tf.data.Dataset.from_tensor_slices((dict(dataframe), labels))
  if shuffle:
    ds = ds.shuffle(buffer_size=len(dataframe))
  ds = ds.batch(batch_size)
  return ds
batch_size = 5 # A small batch sized is used for demonstration purposes
train_ds = df_to_dataset(train, batch_size=batch_size)
val_ds = df_to_dataset(val, shuffle=False, batch_size=batch_size)
test_ds = df_to_dataset(test, shuffle=False, batch_size=batch_size)

 

入力パイプラインを理解する

入力パイプラインを作成した今、それが返すデータのフォーマットを見るためにそれを呼び出しましょう。出力を可読に維持するために小さいバッチサイズを使用しています。

for feature_batch, label_batch in train_ds.take(1):
  print('Every feature:', list(feature_batch.keys()))
  print('A batch of ages:', feature_batch['age'])
  print('A batch of targets:', label_batch )
Every feature: ['fbs', 'ca', 'slope', 'trestbps', 'thalach', 'restecg', 'oldpeak', 'cp', 'age', 'exang', 'thal', 'sex', 'chol']
A batch of ages: tf.Tensor([61 40 67 50 60], shape=(5,), dtype=int32)
A batch of targets: tf.Tensor([0 0 0 0 0], shape=(5,), dtype=int32)

dataset が (dataframe からの) カラム名の辞書を返すことを見ることができます、それは dataframe の行からカラム値へマップします。

 

feature column の幾つかのタイプを実演する

TensorFlow は feature columns の多くのタイプを提供します。このセクションでは、feature column の幾つかのタイプを作成して、それらが dataframe からのカラムをどのように変換するかを実演します。

# We will use this batch to demonstrate several types of feature columns
example_batch = next(iter(train_ds))[0]
# A utility method to create a feature column
# and to transform a batch of data
def demo(feature_column):
  feature_layer = layers.DenseFeatures(feature_column)
  print(feature_layer(example_batch).numpy())

 

Numeric columns

feature column の出力はモデルへの入力になります (上で定義された demo 関数を使用して、dataframe からの各カラムがどのように変換されるかを正確に見ることができます)。numeric column はカラムの最も単純なタイプです。それは実数値の特徴を表わすために使用されます。このカラムを使用するとき、貴方のモデルは (不変の) dataframe からカラム値を受け取ります。

age = feature_column.numeric_column("age")
demo(age)
[[61.]
 [40.]
 [67.]
 [50.]
 [60.]]

心臓疾患データセットでは、dataframe からの殆どのカラムは numeric です。

 

Bucketized columns

しばしば、数値を直接モデルに供給することを望みません、しかし代わりにその値を数値の範囲に基づく異なるカテゴリー分割します。人の年齢を表わす生データを考えます。年齢を numeric column として表わす代わりに、bucketized column を使用して幾つかのバケツに分割できるでしょう。下の one-hot 値が各行がどの年齢範囲にマッチするかを記述していることが分かるでしょう。

age_buckets = feature_column.bucketized_column(age, boundaries=[18, 25, 30, 35, 40, 45, 50, 55, 60, 65])
demo(age_buckets)
[[0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]
 [0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
 [0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]]

 

Categorical columns

このデータセットでは、thal は文字列 (e.g. ‘fixed’, ‘normal’, or ‘reversible’) として表わされます。文字列をモデルに直接には供給できません。代わりに、最初にそれらを数値にマップしなければなりません。categorical 語彙 columns は文字列を one-hot ベクトルとして表わす方法を提供します (上で見た年齢バケツに良く似ています)。語彙は categorical_column_with_vocabulary_list を使用してリストとして渡したり、categorical_column_with_vocabulary_file を使用してファイルからロードすることができます。

thal = feature_column.categorical_column_with_vocabulary_list(
      'thal', ['fixed', 'normal', 'reversible'])

thal_one_hot = feature_column.indicator_column(thal)
demo(thal_one_hot)
WARNING: Logging before flag parsing goes to stderr.
W0614 17:35:53.184868 140449077593856 deprecation.py:323] From /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/feature_column/feature_column_v2.py:2655: add_dispatch_support..wrapper (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.where in 2.0, which has the same broadcast rule as np.where
W0614 17:35:53.188472 140449077593856 deprecation.py:323] From /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/feature_column/feature_column_v2.py:4215: IndicatorColumn._variable_shape (from tensorflow.python.feature_column.feature_column_v2) is deprecated and will be removed in a future version.
Instructions for updating:
The old _FeatureColumn APIs are being deprecated. Please use the new FeatureColumn APIs instead.
W0614 17:35:53.189301 140449077593856 deprecation.py:323] From /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/feature_column/feature_column_v2.py:4270: VocabularyListCategoricalColumn._num_buckets (from tensorflow.python.feature_column.feature_column_v2) is deprecated and will be removed in a future version.
Instructions for updating:
The old _FeatureColumn APIs are being deprecated. Please use the new FeatureColumn APIs instead.

[[0. 0. 1.]
 [0. 0. 1.]
 [0. 0. 1.]
 [0. 0. 1.]
 [0. 0. 1.]]

より複雑なデータセットでは、多くのカラムが categorical (e.g. 文字列) でしょう。Feature columns は categorical データと作業するときにもっとも役立ちます。このデータセットには一つの categorical column があるだけですが、他のデータセットで作業するときに使用できる feature columns の幾つかの重要なタイプを実演するためにそれを使用します。

 

Embedding columns

ごく僅かの可能な文字列を持つ代わりに、カテゴリー毎に数千 (or それ以上) の値を持つことを仮定します。幾つかの理由で、カテゴリー数が巨大になるにつれて、one-hot エンコーディングを使用してニューラルネットワークを訓練することは実行不可能になります。この制限を打開するために embedding column を使用することができます。データを多くの次元の one-hot ベクトルとして表わす代わりに、embedding column はそのデータをより低次元な、密ベクトルとして表します、そこでは各セルは (単に 0 か 1 ではなく) 任意の数字を含むことができます。embedding のサイズ (下の例では 8) は調整しなければならないパラメータです。

Key point: categorical column が多くの可能な値を持つときに embedding column を使用することが最善です。ここでは実演目的で一つを使用していますので、将来的に異なるデータセットのために変更可能な完全なサンプルを貴方は持つことになります。

# Notice the input to the embedding column is the categorical column
# we previously created
thal_embedding = feature_column.embedding_column(thal, dimension=8)
demo(thal_embedding)
[[ 0.13279669  0.19413401 -0.69587415 -0.6805197   0.3184564   0.45431668
  -0.13196784 -0.57410216]
 [ 0.13279669  0.19413401 -0.69587415 -0.6805197   0.3184564   0.45431668
  -0.13196784 -0.57410216]
 [ 0.13279669  0.19413401 -0.69587415 -0.6805197   0.3184564   0.45431668
  -0.13196784 -0.57410216]
 [ 0.13279669  0.19413401 -0.69587415 -0.6805197   0.3184564   0.45431668
  -0.13196784 -0.57410216]
 [ 0.13279669  0.19413401 -0.69587415 -0.6805197   0.3184564   0.45431668
  -0.13196784 -0.57410216]]

 

Hashed feature columns

非常に多数の値を持つ categorical column を表わすもう一つの方法は categorical_column_with_hash_bucket を使用することです。この feature column は入力のハッシュ値を計算し、それから文字列をエンコードするために hash_bucket_size バケツの一つを選択します。この column を使用するとき、語彙を提供する必要はなく、そして空間をセーブするために実際のカテゴリの数よりも hash_buckets の数を小さくすることを選択できます。

Key point: このテクニックの重要な不都合な点は異なる文字列が同じバケツにマップされる衝突があるかもしれないことです。実際には、これは幾つかのデータセットに対して関係なく上手く動作します。

thal_hashed = feature_column.categorical_column_with_hash_bucket(
      'thal', hash_bucket_size=1000)
demo(feature_column.indicator_column(thal_hashed))
W0614 17:35:53.226184 140449077593856 deprecation.py:323] From /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/feature_column/feature_column_v2.py:4270: HashedCategoricalColumn._num_buckets (from tensorflow.python.feature_column.feature_column_v2) is deprecated and will be removed in a future version.
Instructions for updating:
The old _FeatureColumn APIs are being deprecated. Please use the new FeatureColumn APIs instead.

[[0. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]]

 

Crossed feature columns

feature cross として良く知られる、特徴 (群) を単一の特徴に結合することはモデルに特徴の各組み合わせについて個別の重みを学習することを可能にします。ここで、age と thal のクロスである新しい特徴を作成します。crossed_column は総ての可能な組み合わせの完全なテーブルを構築はしないことに注意してください (それは非常に巨大でしょう)。代わりに、それは hashed_column により支援されますので、テーブルがどのくらい巨大であるかを選択できます。

crossed_feature = feature_column.crossed_column([age_buckets, thal], hash_bucket_size=1000)
demo(feature_column.indicator_column(crossed_feature))
W0614 17:35:53.243324 140449077593856 deprecation.py:323] From /tmpfs/src/tf_docs_env/lib/python3.5/site-packages/tensorflow/python/feature_column/feature_column_v2.py:4270: CrossedColumn._num_buckets (from tensorflow.python.feature_column.feature_column_v2) is deprecated and will be removed in a future version.
Instructions for updating:
The old _FeatureColumn APIs are being deprecated. Please use the new FeatureColumn APIs instead.

[[0. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]]

 

どの column を使用するか選択する

feature columns の幾つかのタイプをどのように使用するか見てきました。今はそれらをモデルを訓練するために使用します。このチュートリアルのゴールは feature column で作業するために必要な完全なコード (e.g. mechanics) を示すことです。下のモデルを訓練するために幾つかの column を任意に選択しました。

Key point: 貴方の目的が正確なモデルを構築することであれば、貴方自身のより大きなデータセット試してください、そしてどの特徴が含めるために最も意味があるか、そしてそれらがどのように表わされるべきかを注意深く考えてください。

feature_columns = []

# numeric cols
for header in ['age', 'trestbps', 'chol', 'thalach', 'oldpeak', 'slope', 'ca']:
  feature_columns.append(feature_column.numeric_column(header))

# bucketized cols
age_buckets = feature_column.bucketized_column(age, boundaries=[18, 25, 30, 35, 40, 45, 50, 55, 60, 65])
feature_columns.append(age_buckets)

# indicator cols
thal = feature_column.categorical_column_with_vocabulary_list(
      'thal', ['fixed', 'normal', 'reversible'])
thal_one_hot = feature_column.indicator_column(thal)
feature_columns.append(thal_one_hot)

# embedding cols
thal_embedding = feature_column.embedding_column(thal, dimension=8)
feature_columns.append(thal_embedding)

# crossed cols
crossed_feature = feature_column.crossed_column([age_buckets, thal], hash_bucket_size=1000)
crossed_feature = feature_column.indicator_column(crossed_feature)
feature_columns.append(crossed_feature)

 

feature 層を作成する

私達の feature columns を定義した今、それらを Keras モデルへ入力するために DenseFeatures を使用します。

feature_layer = tf.keras.layers.DenseFeatures(feature_columns)

先程は、feature columns がどのように動作したかを示すために小さいバッチサイズを使用しました。より大きなバッチサイズを持つ新しい入力パイプラインを作成します。

batch_size = 32
train_ds = df_to_dataset(train, batch_size=batch_size)
val_ds = df_to_dataset(val, shuffle=False, batch_size=batch_size)
test_ds = df_to_dataset(test, shuffle=False, batch_size=batch_size)

 

モデルを作成、コンパイルそして訓練する

model = tf.keras.Sequential([
  feature_layer,
  layers.Dense(128, activation='relu'),
  layers.Dense(128, activation='relu'),
  layers.Dense(1, activation='sigmoid')
])

model.compile(optimizer='adam',
              loss='binary_crossentropy',
              metrics=['accuracy'],
              run_eagerly=True)

model.fit(train_ds,
          validation_data=val_ds,
          epochs=5)
Epoch 1/5
7/7 [==============================] - 1s 142ms/step - loss: 2.5852 - accuracy: 0.5992 - val_loss: 1.5481 - val_accuracy: 0.6735
Epoch 2/5
7/7 [==============================] - 0s 30ms/step - loss: 1.4630 - accuracy: 0.5475 - val_loss: 0.8428 - val_accuracy: 0.6735
Epoch 3/5
7/7 [==============================] - 0s 31ms/step - loss: 0.6788 - accuracy: 0.7359 - val_loss: 0.8275 - val_accuracy: 0.6531
Epoch 4/5
7/7 [==============================] - 0s 30ms/step - loss: 0.8789 - accuracy: 0.6067 - val_loss: 0.7656 - val_accuracy: 0.6327
Epoch 5/5
7/7 [==============================] - 0s 32ms/step - loss: 0.6756 - accuracy: 0.6843 - val_loss: 0.7049 - val_accuracy: 0.6735

<tensorflow.python.keras.callbacks.History at 0x7fbccd338668>
loss, accuracy = model.evaluate(test_ds)
print("Accuracy", accuracy)
2/2 [==============================] - 0s 18ms/step - loss: 0.4494 - accuracy: 0.7869
Accuracy 0.78688526

 
Key point: 典型的には遥かに巨大でより複雑なデータセットを伴う深層学習で最善の結果を見るでしょう。この一つのように小さいデータセットで作業するときは、決定木やランダムフォレストを強力なベースラインとして使用することを推奨します。このチュートリアルの目標は正確なモデルを訓練することではなく、構造化データで作業するメカニクスを実演することですので、将来的に貴方自身のデータセットで作業するときの開始点として使用するコードを貴方は持っています。

 

以上



AI導入支援 #2 ウェビナー

スモールスタートを可能としたAI導入支援   Vol.2
[無料 WEB セミナー] [詳細]
「画像認識 AI PoC スターターパック」の紹介
既に AI 技術を実ビジネスで活用し、成果を上げている日本企業も多く存在しており、競争優位なビジネスを展開しております。
しかしながら AI を導入したくとも PoC (概念実証) だけでも高額な費用がかかり取組めていない企業も少なくないようです。A I導入時には欠かせない PoC を手軽にしかも短期間で認知度を確認可能とするサービの紹介と共に、AI 技術の特性と具体的な導入プロセスに加え運用時のポイントについても解説いたします。
日時:2021年10月13日(水)
会場:WEBセミナー
共催:クラスキャット、日本FLOW(株)
後援:働き方改革推進コンソーシアム
参加費: 無料 (事前登録制)
人工知能開発支援
◆ クラスキャットは 人工知能研究開発支援 サービスを提供しています :
  • テクニカルコンサルティングサービス
  • 実証実験 (プロトタイプ構築)
  • アプリケーションへの実装
  • 人工知能研修サービス
◆ お問合せ先 ◆
(株)クラスキャット
セールス・インフォメーション
E-Mail:sales-info@classcat.com