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

Hub with Keras

Note: これらのドキュメントは私たちTensorFlowコミュニティが翻訳したものです。コミュニティによる 翻訳はベストエフォートであるため、この翻訳が正確であることや英語の公式ドキュメントの 最新の状態を反映したものであることを保証することはできません。 この翻訳の品質を向上させるためのご意見をお持ちの方は、GitHubリポジトリtensorflow/docsにプルリクエストをお送りください。 コミュニティによる翻訳やレビューに参加していただける方は、 docs-ja@tensorflow.org メーリングリストにご連絡ください。

TensorFlow Hub は事前学習済みモデルのコンポーネントを共有する一つの方法です。事前学習済みモデルの検索可能なリストは TensorFlow Module Hub をご覧ください。

このチュートリアルでは以下の事を説明します。

  1. tf.keras での TensorFlow Hub の使い方
  2. TensorFlow Hub を使って画像を分類する方法
  3. シンプルな転移学習の方法

セットアップ

インポート


In [ ]:
!pip install -U tensorflow_hub

In [ ]:
import matplotlib.pylab as plt

import tensorflow.compat.v1 as tf

In [ ]:
import tensorflow_hub as hub

from tensorflow.keras import layers

ImageNet 分類器

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

mobilenet をロードするには hub.module を、それを keras レイヤーとしてまとめるには tf.keras.layers.Lambda を使ってください。

tfhub.dev にある TensorFlow 1.x の画像分類器 URL であればすべてここで動作します。


In [ ]:
classifier_url ="https://tfhub.dev/google/tf2-preview/mobilenet_v2/classification/2" #@param {type:"string"}

In [ ]:
IMAGE_SHAPE = (224, 224)

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

単一の画像で実行する

モデルを試すために単一の画像をダウンロードしてください。


In [ ]:
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

In [ ]:
grace_hopper = np.array(grace_hopper)/255.0
grace_hopper.shape

バッチ次元を一つ追加し、画像をモデルに渡してください。


In [ ]:
result = classifier.predict(grace_hopper[np.newaxis, ...])
result.shape

結果は 1001 の要素をもつロジットベクトルで、画像がそれぞれのクラスに属する確率を表します。

そのため、もっとも確率の高いクラスの ID は argmax でみつけることができます。


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

予測結果をデコードする

予測されたクラスの ID を ImageNet のラベルと突き合わせて、予測結果をデコードします。


In [ ]:
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())

In [ ]:
plt.imshow(grace_hopper)
plt.axis('off')
predicted_class_name = imagenet_labels[predicted_class]
_ = plt.title("Prediction: " + predicted_class_name.title())

シンプルな転移学習

TF Hub を利用すると、私たちが用意したデータセット内のクラスを認識するために、モデルの最上位層を再学習する事が容易になります。

データセット

この例では、TensorFlow の花データセットを使用します。


In [ ]:
data_root = tf.keras.utils.get_file(
  'flower_photos','https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz',
   untar=True)

このデータをモデルに読み込むもっとも簡単な方法は、 tf.keras.preprocessing.image.ImageDataGenerator を使用することです。

TensorFlow Hub のすべての画像モジュールは、 [0, 1] の範囲の float 入力を想定しています。これを実現するには ImageDataGenerator の rescale パラメータを使用してください。

画像のサイズについては後で処理されます。


In [ ]:
image_generator = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1/255)
image_data = image_generator.flow_from_directory(str(data_root), target_size=IMAGE_SHAPE)

結果のオブジェクトは、image_batch と label_batch のペアを返すイテレータです。


In [ ]:
for image_batch, label_batch in image_data:
  print("Image batch shape: ", image_batch.shape)
  print("Label batch shape: ", label_batch.shape)
  break

画像のバッチに対して分類器を実行する

それでは、画像のバッチで分類器を実行します。


In [ ]:
result_batch = classifier.predict(image_batch)
result_batch.shape

In [ ]:
predicted_class_names = imagenet_labels[np.argmax(result_batch, axis=-1)]
predicted_class_names

これらの予測結果と実際の画像がどの程度一致しているか確認してください。


In [ ]:
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 ファイルを参照してください。

結果は完全には程遠いですが、これらがこのモデルのために学習されたクラスでない事を考えると悪くはないです( 「デイジー」を除く)。

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

TensorFlow Hub はさらに、最上位の分類層がないモデルを配布することもしています。これらは転移学習を簡単に行うために使用することができます。

tfhub.dev からの TensorFlow 1.x の画像特徴ベクトルの URL はすべてここで動作します。


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

モジュールを作成し、入力として期待される画像サイズを確認します。


In [ ]:
feature_extractor_layer = hub.KerasLayer(feature_extractor_url,
                                         input_shape=(224,224,3))

特徴抽出器は、各画像に対して 1280 要素のベクトルを返します。


In [ ]:
feature_batch = feature_extractor_layer(image_batch)
print(feature_batch.shape)

学習が新しい分類層のみを修正するように、特徴抽出層の変数を freeze(固定)します。


In [ ]:
feature_extractor_layer.trainable = False

分類ヘッドを追加する

hub レイヤーを tf.keras.Sequential モデルでラップし、新しい分類層を一つ追加します。


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

model.summary()

In [ ]:
predictions = model(image_batch)

In [ ]:
predictions.shape

モデルを学習する

学習プロセスを構成するために、 compile を使用してください。


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

今度は .fit メソッドを使ってモデルを学習します。

この例を短くするために、2エポックだけ学習を実行します。学習の進行状況を可視化するには、エポック平均ではなく、カスタムコールバックを使用して各バッチの損失と精度を個別に記録します。


In [ ]:
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()

In [ ]:
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])

これで、ほんの数回の学習の繰り返しでも、モデルがタスクを進めていることがわかったと思います。


In [ ]:
plt.figure()
plt.ylabel("Loss")
plt.xlabel("Training Steps")
plt.ylim([0,2])
plt.plot(batch_stats_callback.batch_losses)

In [ ]:
plt.figure()
plt.ylabel("Accuracy")
plt.xlabel("Training Steps")
plt.ylim([0,1])
plt.plot(batch_stats_callback.batch_acc)

予測結果を確認する

前からプロットをやり直すには、まずクラス名の順序付きリストを取得します。


In [ ]:
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

モデルを介してイメージバッチを実行し、インデックスをクラス名に変換します。


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

結果をプロットします。


In [ ]:
label_id = np.argmax(label_batch, axis=-1)

In [ ]:
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 としてエクスポートします。


In [ ]:
import time
t = time.time()

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

export_path

それではモデルをリロードできることを確認してください。リロードしたモデルでもおなじ結果が得られます。


In [ ]:
reloaded = tf.keras.experimental.load_from_saved_model(export_path, custom_objects={'KerasLayer':hub.KerasLayer})

In [ ]:
result_batch = model.predict(image_batch)
reloaded_result_batch = reloaded.predict(image_batch)

In [ ]:
abs(reloaded_result_batch - result_batch).max()

この saved model は後で推論のためにロードするか、もしくは TFLiteTFjs.に変換することができます。