Google I / Oの基調講演、製品セッション、ワークショップなどを見るプレイリストを見る

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
tf.__version__
'2.5.0'

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

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([ 5 12 64  3  3], shape=(5,), dtype=int64)
A batch of targets: tf.Tensor([1 1 1 0 0], 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([[ 0.44953057],
       [-0.50171113],
       [-0.50171113],
       [-0.8187917 ],
       [-0.50171113]], 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_tokens=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(num_tokens=index.vocabulary_size())

  # 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., 1., 0.],
       [0., 0., 1., 0.],
       [0., 0., 1., 0.],
       [0., 0., 1., 0.],
       [0., 0., 0., 1.]], 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., 1., 0., 0., 0.],
       [0., 1., 0., 0., 0.],
       [0., 1., 0., 0., 0.],
       [0., 0., 0., 1., 0.],
       [0., 0., 0., 1., 0.]], 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 [==============================] - 2s 21ms/step - loss: 0.6548 - accuracy: 0.5280 - val_loss: 0.5855 - val_accuracy: 0.7205
Epoch 2/10
29/29 [==============================] - 0s 8ms/step - loss: 0.5997 - accuracy: 0.6510 - val_loss: 0.5631 - val_accuracy: 0.7329
Epoch 3/10
29/29 [==============================] - 0s 8ms/step - loss: 0.5809 - accuracy: 0.6664 - val_loss: 0.5483 - val_accuracy: 0.7411
Epoch 4/10
29/29 [==============================] - 0s 8ms/step - loss: 0.5673 - accuracy: 0.6831 - val_loss: 0.5379 - val_accuracy: 0.7476
Epoch 5/10
29/29 [==============================] - 0s 8ms/step - loss: 0.5576 - accuracy: 0.7064 - val_loss: 0.5297 - val_accuracy: 0.7481
Epoch 6/10
29/29 [==============================] - 0s 8ms/step - loss: 0.5503 - accuracy: 0.7082 - val_loss: 0.5252 - val_accuracy: 0.7497
Epoch 7/10
29/29 [==============================] - 0s 8ms/step - loss: 0.5447 - accuracy: 0.7168 - val_loss: 0.5213 - val_accuracy: 0.7503
Epoch 8/10
29/29 [==============================] - 0s 8ms/step - loss: 0.5395 - accuracy: 0.7189 - val_loss: 0.5179 - val_accuracy: 0.7530
Epoch 9/10
29/29 [==============================] - 0s 8ms/step - loss: 0.5351 - accuracy: 0.7154 - val_loss: 0.5158 - val_accuracy: 0.7524
Epoch 10/10
29/29 [==============================] - 0s 8ms/step - loss: 0.5335 - accuracy: 0.7290 - val_loss: 0.5135 - val_accuracy: 0.7514
<tensorflow.python.keras.callbacks.History at 0x7f5d8b9a6dd0>
loss, accuracy = model.evaluate(test_ds)
print("Accuracy", accuracy)
10/10 [==============================] - 0s 5ms/step - loss: 0.5169 - accuracy: 0.7396
Accuracy 0.7396013736724854

新しいデータに関する推論

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

model.save('my_pet_classifier')
reloaded_model = tf.keras.models.load_model('my_pet_classifier')
WARNING:absl:Function `_wrapped_model` contains input name(s) PhotoAmt, Fee, Age, Type, Color1, Color2, Gender, MaturitySize, FurLength, Vaccinated, Sterilized, Health, Breed1 with unsupported characters which will be renamed to photoamt, fee, age, type, color1, color2, gender, maturitysize, furlength, vaccinated, sterilized, health, breed1 in the SavedModel.
INFO:tensorflow:Assets written to: my_pet_classifier/assets
INFO:tensorflow:Assets written to: my_pet_classifier/assets

新しいサンプルの予測を取得するには、 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 86.0 percent probability of getting adopted.

次のステップ

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