今日のローカルTensorFlowEverywhereイベントの出欠確認!
このページは Cloud Translation API によって翻訳されました。
Switch to English

Keras前処理レイヤーを使用して構造化データを分類する

TensorFlow.orgで表示 GoogleColabで実行 GitHubでソースを表示ノートブックをダウンロード

このチュートリアルでは、構造化データ(CSVの表形式データなど)を分類する方法を示します。 Kerasを使用してモデルを定義し、CSVの列からモデルのトレーニングに使用される機能にマップするためのブリッジとしてレイヤー前処理します。このチュートリアルには、次の完全なコードが含まれています。

  • Pandasを使用してCSVファイルを読み込みます。
  • tf.dataを使用して行をバッチ処理およびシャッフルするための入力パイプラインを構築します。
  • CSVの列から、Keras前処理レイヤーを使用してモデルをトレーニングするために使用される機能にマップします。
  • Kerasを使用してモデルを構築、トレーニング、および評価します。

データセット

PetFinderデータセットの簡易バージョンを使用します。 CSVには数千行あります。各行はペットを説明し、各列は属性を説明します。この情報を使用して、ペットが養子縁組されるかどうかを予測します。

以下は、このデータセットの説明です。数値列とカテゴリ列の両方があることに注意してください。このチュートリアルでは使用しないフリーテキスト列があります。

カラム説明機能タイプデータ・タイプ
タイプ動物の種類(犬、猫)カテゴリカルストリング
年齢ペットの年齢数値整数
品種1ペットの主な品種カテゴリカルストリング
Color1ペットのカラー1カテゴリカルストリング
Color2ペットのカラー2カテゴリカルストリング
MaturitySize成熟時のサイズカテゴリカルストリング
FurLength毛皮の長さカテゴリカルストリング
予防接種ペットは予防接種を受けていますカテゴリカルストリング
滅菌済みペットは滅菌済みカテゴリカルストリング
健康健康状態カテゴリカルストリング
費用採用料数値整数
説明このペットのプロフィール記事テキストストリング
PhotoAmtこのペットのアップロードされた写真の合計数値整数
AdoptionSpeed採用のスピード分類整数

TensorFlowおよびその他のライブラリをインポートする

pip install -q sklearn
import numpy as np
import pandas as pd
import tensorflow as tf

from sklearn.model_selection import train_test_split
from tensorflow.keras import layers
from tensorflow.keras.layers.experimental import preprocessing

パンダを使用してデータフレームを作成する

Pandasは、構造化データのロードと操作に役立つ多くのユーティリティを備えたPythonライブラリです。 Pandasを使用してURLからデータセットをダウンロードし、データフレームにロードします。

import pathlib

dataset_url = 'http://storage.googleapis.com/download.tensorflow.org/data/petfinder-mini.zip'
csv_file = 'datasets/petfinder-mini/petfinder-mini.csv'

tf.keras.utils.get_file('petfinder_mini.zip', dataset_url,
                        extract=True, cache_dir='.')
dataframe = pd.read_csv(csv_file)
Downloading data from http://storage.googleapis.com/download.tensorflow.org/data/petfinder-mini.zip
1671168/1668792 [==============================] - 0s 0us/step

dataframe.head()

ターゲット変数を作成する

Kaggleコンテストのタスクは、ペットが採用される速度を予測することです(たとえば、最初の週、最初の月、最初の3か月など)。チュートリアルのためにこれを単純化しましょう。ここでは、これをバイナリ分類問題に変換し、ペットが養子縁組されたかどうかを簡単に予測します。

ラベル列を変更した後、0はペットが養子縁組されなかったことを示し、1は養子縁組されたことを示します。

# In the original dataset "4" indicates the pet was not adopted.
dataframe['target'] = np.where(dataframe['AdoptionSpeed']==4, 0, 1)

# Drop un-used columns.
dataframe = dataframe.drop(columns=['AdoptionSpeed', 'Description'])

データフレームをトレーニング、検証、テストに分割します

ダウンロードしたデータセットは単一の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')
7383 train examples
1846 validation examples
2308 test examples

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

次に、データをシャッフルしてバッチ処理するために、データフレームをtf.dataでラップします。非常に大きな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)
  ds = ds.prefetch(batch_size)
  return ds

入力パイプラインを作成したので、それを呼び出して、返されるデータの形式を確認しましょう。出力を読みやすくするために、小さなバッチサイズを使用しました。

batch_size = 5
train_ds = df_to_dataset(train, batch_size=batch_size)
[(train_features, label_batch)] = train_ds.take(1)
print('Every feature:', list(train_features.keys()))
print('A batch of ages:', train_features['Age'])
print('A batch of targets:', label_batch )
Every feature: ['Type', 'Age', 'Breed1', 'Gender', 'Color1', 'Color2', 'MaturitySize', 'FurLength', 'Vaccinated', 'Sterilized', 'Health', 'Fee', 'PhotoAmt']
A batch of ages: tf.Tensor([ 2  2 18  2  1], shape=(5,), dtype=int64)
A batch of targets: tf.Tensor([1 1 0 1 1], shape=(5,), dtype=int64)

データセットが、データフレーム内の行の列値にマップされる(データフレームからの)列名のディクショナリを返すことがわかります。

前処理レイヤーの使用を示します。

Keras前処理レイヤーAPIを使用すると、Kerasネイティブの入力処理パイプラインを構築できます。 3つの前処理レイヤーを使用して、機能の前処理コードを示します。

  • Normalization -データの機能ごとの正規化。
  • CategoryEncodingカテゴリエンコーディングレイヤー。
  • StringLookup文字列を語彙から整数インデックスにマップします。
  • IntegerLookupボキャブラリーから整数インデックスに整数をマップします。

利用可能な前処理レイヤーのリストはここにあります

数値列

数値フィーチャごとに、Normalization()レイヤーを使用して、各フィーチャの平均が0で、標準偏差が1であることを確認します。

get_normalization_layer関数は、数値特徴に特徴get_normalization_layer正規化を適用するレイヤーを返します。

def get_normalization_layer(name, dataset):
  # Create a Normalization layer for our feature.
  normalizer = preprocessing.Normalization()

  # Prepare a Dataset that only yields our feature.
  feature_ds = dataset.map(lambda x, y: x[name])

  # Learn the statistics of the data.
  normalizer.adapt(feature_ds)

  return normalizer
photo_count_col = train_features['PhotoAmt']
layer = get_normalization_layer('PhotoAmt', train_ds)
layer(photo_count_col)
<tf.Tensor: shape=(5, 1), dtype=float32, numpy=
array([[ 1.045485  ],
       [-1.1339161 ],
       [-0.19988704],
       [ 0.11145599],
       [ 0.42279902]], dtype=float32)>

カテゴリ列

このデータセットでは、Typeは文字列として表されます(例:「Dog」または「Cat」)。文字列をモデルに直接フィードすることはできません。前処理レイヤーは、文字列をワンホットベクトルとして表現します。

get_category_encoding_layer関数は、値を語彙から整数インデックスにマップし、機能をワンホットエンコードするレイヤーを返します。

def get_category_encoding_layer(name, dataset, dtype, max_tokens=None):
  # Create a StringLookup layer which will turn strings into integer indices
  if dtype == 'string':
    index = preprocessing.StringLookup(max_tokens=max_tokens)
  else:
    index = preprocessing.IntegerLookup(max_values=max_tokens)

  # Prepare a Dataset that only yields our feature
  feature_ds = dataset.map(lambda x, y: x[name])

  # Learn the set of possible values and assign them a fixed integer index.
  index.adapt(feature_ds)

  # Create a Discretization for our integer indices.
  encoder = preprocessing.CategoryEncoding(max_tokens=index.vocab_size())

  # Prepare a Dataset that only yields our feature.
  feature_ds = feature_ds.map(index)

  # Learn the space of possible indices.
  encoder.adapt(feature_ds)

  # Apply one-hot encoding to our indices. The lambda function captures the
  # layer so we can use them, or include them in the functional model later.
  return lambda feature: encoder(index(feature))
type_col = train_features['Type']
layer = get_category_encoding_layer('Type', train_ds, 'string')
layer(type_col)
<tf.Tensor: shape=(5, 4), dtype=float32, numpy=
array([[0., 0., 0., 1.],
       [0., 0., 0., 1.],
       [0., 0., 1., 0.],
       [0., 0., 0., 1.],
       [0., 0., 1., 0.]], dtype=float32)>

多くの場合、モデルに直接数値を入力するのではなく、それらの入力のワンホットエンコーディングを使用します。ペットの年齢を表す生データを検討してください。

type_col = train_features['Age']
category_encoding_layer = get_category_encoding_layer('Age', train_ds,
                                                      'int64', 5)
category_encoding_layer(type_col)
<tf.Tensor: shape=(5, 5), dtype=float32, numpy=
array([[0., 0., 1., 0., 0.],
       [0., 0., 1., 0., 0.],
       [0., 1., 0., 0., 0.],
       [0., 0., 1., 0., 0.],
       [0., 0., 0., 0., 1.]], dtype=float32)>

使用する列を選択します

いくつかのタイプの前処理レイヤーを使用する方法を見てきました。次に、それらを使用してモデルをトレーニングします。 Keras機能API使用してモデルを構築します。 Keras機能APIは、 tf.keras.SequentialAPIよりも柔軟なモデルを作成する方法です。

このチュートリアルの目的は、前処理レイヤーを操作するために必要な完全なコード(メカニックなど)を示すことです。モデルをトレーニングするために、いくつかの列が任意に選択されています。

以前は、入力パイプラインを示すために小さなバッチサイズを使用しました。次に、バッチサイズを大きくした新しい入力パイプラインを作成しましょう。

batch_size = 256
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)
all_inputs = []
encoded_features = []

# Numeric features.
for header in ['PhotoAmt', 'Fee']:
  numeric_col = tf.keras.Input(shape=(1,), name=header)
  normalization_layer = get_normalization_layer(header, train_ds)
  encoded_numeric_col = normalization_layer(numeric_col)
  all_inputs.append(numeric_col)
  encoded_features.append(encoded_numeric_col)
# Categorical features encoded as integers.
age_col = tf.keras.Input(shape=(1,), name='Age', dtype='int64')
encoding_layer = get_category_encoding_layer('Age', train_ds, dtype='int64',
                                             max_tokens=5)
encoded_age_col = encoding_layer(age_col)
all_inputs.append(age_col)
encoded_features.append(encoded_age_col)
# Categorical features encoded as string.
categorical_cols = ['Type', 'Color1', 'Color2', 'Gender', 'MaturitySize',
                    'FurLength', 'Vaccinated', 'Sterilized', 'Health', 'Breed1']
for header in categorical_cols:
  categorical_col = tf.keras.Input(shape=(1,), name=header, dtype='string')
  encoding_layer = get_category_encoding_layer(header, train_ds, dtype='string',
                                               max_tokens=5)
  encoded_categorical_col = encoding_layer(categorical_col)
  all_inputs.append(categorical_col)
  encoded_features.append(encoded_categorical_col)

モデルを作成、コンパイル、トレーニングします

これで、エンドツーエンドモデルを作成できます。

all_features = tf.keras.layers.concatenate(encoded_features)
x = tf.keras.layers.Dense(32, activation="relu")(all_features)
x = tf.keras.layers.Dropout(0.5)(x)
output = tf.keras.layers.Dense(1)(x)
model = tf.keras.Model(all_inputs, output)
model.compile(optimizer='adam',
              loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=["accuracy"])

接続グラフを視覚化してみましょう。

# rankdir='LR' is used to make the graph horizontal.
tf.keras.utils.plot_model(model, show_shapes=True, rankdir="LR")

png

モデルをトレーニングする

model.fit(train_ds, epochs=10, validation_data=val_ds)
Epoch 1/10
29/29 [==============================] - 0s 15ms/step - loss: 0.6088 - accuracy: 0.5924 - val_loss: 0.5623 - val_accuracy: 0.7302
Epoch 2/10
29/29 [==============================] - 0s 6ms/step - loss: 0.5766 - accuracy: 0.6833 - val_loss: 0.5459 - val_accuracy: 0.7356
Epoch 3/10
29/29 [==============================] - 0s 6ms/step - loss: 0.5579 - accuracy: 0.6981 - val_loss: 0.5364 - val_accuracy: 0.7362
Epoch 4/10
29/29 [==============================] - 0s 6ms/step - loss: 0.5467 - accuracy: 0.7127 - val_loss: 0.5287 - val_accuracy: 0.7313
Epoch 5/10
29/29 [==============================] - 0s 6ms/step - loss: 0.5401 - accuracy: 0.7059 - val_loss: 0.5240 - val_accuracy: 0.7329
Epoch 6/10
29/29 [==============================] - 0s 6ms/step - loss: 0.5362 - accuracy: 0.7222 - val_loss: 0.5203 - val_accuracy: 0.7313
Epoch 7/10
29/29 [==============================] - 0s 6ms/step - loss: 0.5290 - accuracy: 0.7189 - val_loss: 0.5172 - val_accuracy: 0.7297
Epoch 8/10
29/29 [==============================] - 0s 6ms/step - loss: 0.5273 - accuracy: 0.7172 - val_loss: 0.5151 - val_accuracy: 0.7286
Epoch 9/10
29/29 [==============================] - 0s 6ms/step - loss: 0.5236 - accuracy: 0.7210 - val_loss: 0.5127 - val_accuracy: 0.7291
Epoch 10/10
29/29 [==============================] - 0s 6ms/step - loss: 0.5200 - accuracy: 0.7219 - val_loss: 0.5112 - val_accuracy: 0.7297

<tensorflow.python.keras.callbacks.History at 0x7f6fc8e61f98>
loss, accuracy = model.evaluate(test_ds)
print("Accuracy", accuracy)
10/10 [==============================] - 0s 4ms/step - loss: 0.5232 - accuracy: 0.7370
Accuracy 0.7370017170906067

新しいデータの推測

これで、Kerasモデルを保存して再ロードできます。 TensorFlowモデルの詳細については、こちらのチュートリアルに従ってください。

model.save('my_pet_classifier')
reloaded_model = tf.keras.models.load_model('my_pet_classifier')
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/tracking.py:111: Model.state_updates (from tensorflow.python.keras.engine.training) is deprecated and will be removed in a future version.
Instructions for updating:
This property should not be used in TensorFlow 2.0, as updates are applied automatically.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/tracking.py:111: Layer.updates (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.
Instructions for updating:
This property should not be used in TensorFlow 2.0, as updates are applied automatically.
INFO:tensorflow:Assets written to: my_pet_classifier/assets
WARNING:tensorflow:5 out of the last 5 calls to <function recreate_function.<locals>.restored_function_body at 0x7f6fc8b82400> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for  more details.
WARNING:tensorflow:6 out of the last 6 calls to <function recreate_function.<locals>.restored_function_body at 0x7f6fc8b82d08> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for  more details.
WARNING:tensorflow:7 out of the last 7 calls to <function recreate_function.<locals>.restored_function_body at 0x7f6fc8b611e0> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for  more details.
WARNING:tensorflow:8 out of the last 8 calls to <function recreate_function.<locals>.restored_function_body at 0x7f6fc8b857b8> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for  more details.
WARNING:tensorflow:9 out of the last 9 calls to <function recreate_function.<locals>.restored_function_body at 0x7f6f6870b158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for  more details.
WARNING:tensorflow:10 out of the last 10 calls to <function recreate_function.<locals>.restored_function_body at 0x7f6f6870bd08> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for  more details.
WARNING:tensorflow:11 out of the last 11 calls to <function recreate_function.<locals>.restored_function_body at 0x7f6fc8b6abf8> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for  more details.

新しいサンプルの予測を取得するには、 model.predict()呼び出すだけです。あなたがする必要があるのは2つだけです:

  1. バッチディメンションを持つようにスカラーをリストにラップします(モデルはデータのバッチのみを処理し、単一のサンプルは処理しません)
  2. 各機能でconvert_to_tensorを呼び出しconvert_to_tensor
sample = {
    'Type': 'Cat',
    'Age': 3,
    'Breed1': 'Tabby',
    'Gender': 'Male',
    'Color1': 'Black',
    'Color2': 'White',
    'MaturitySize': 'Small',
    'FurLength': 'Short',
    'Vaccinated': 'No',
    'Sterilized': 'No',
    'Health': 'Healthy',
    'Fee': 100,
    'PhotoAmt': 2,
}

input_dict = {name: tf.convert_to_tensor([value]) for name, value in sample.items()}
predictions = reloaded_model.predict(input_dict)
prob = tf.nn.sigmoid(predictions[0])

print(
    "This particular pet had a %.1f percent probability "
    "of getting adopted." % (100 * prob)
)
This particular pet had a 83.4 percent probability of getting adopted.

次のステップ

構造化データの分類についてさらに学ぶための最良の方法は、自分で試すことです。使用する別のデータセットを見つけて、上記のようなコードを使用してモデルを分類するようにトレーニングすることをお勧めします。精度を向上させるには、モデルに含める機能と、それらをどのように表現するかを慎重に検討してください。