Klasyfikuj uporządkowane dane za pomocą warstw przetwarzania wstępnego Keras

Zobacz na TensorFlow.org Uruchom w Google Colab Wyświetl źródło na GitHub Pobierz notatnik

Ten samouczek pokazuje, jak klasyfikować dane strukturalne, takie jak dane tabelaryczne, przy użyciu uproszczonej wersji zestawu danych PetFinder z konkursu Kaggle przechowywanego w pliku CSV.

Użyjesz Keras do zdefiniowania modelu, a warstwy przetwarzania wstępnego Keras jako pomost do mapowania kolumn w pliku CSV do obiektów używanych do uczenia modelu. Celem jest przewidzenie, czy zwierzę zostanie adoptowane.

Ten samouczek zawiera kompletny kod dla:

  • Ładowanie pliku CSV do DataFrame przy użyciu pand .
  • Tworzenie potoku wejściowego do partii i tasowania wierszy przy użyciu tf.data . (Odwiedź tf.data: Buduj potoki wejściowe TensorFlow, aby uzyskać więcej informacji).
  • Mapowanie z kolumn w pliku CSV do funkcji używanych do trenowania modelu za pomocą warstw przetwarzania wstępnego Keras.
  • Budowanie, trenowanie i ocena modelu przy użyciu wbudowanych metod Keras.

Minizestaw danych PetFinder.my

Istnieje kilka tysięcy wierszy w pliku danych CSV PetFinder.my mini, gdzie każdy wiersz opisuje zwierzaka (psa lub kota), a każda kolumna opisuje atrybut, taki jak wiek, rasa, kolor i tak dalej.

Zwróć uwagę, że w poniższym podsumowaniu zbioru danych znajdują się głównie kolumny liczbowe i kategorialne. W tym samouczku będziesz miał do czynienia tylko z tymi dwoma typami funkcji, upuszczając Description (funkcja dowolnego tekstu) i AdoptionSpeed (funkcja klasyfikacji) podczas wstępnego przetwarzania danych.

Kolumna Opis zwierzaka Typ funkcji Typ danych
Type Rodzaj zwierzęcia ( Dog , Cat ) Kategoryczny Strunowy
Age Wiek Liczbowy Liczba całkowita
Breed1 Rasa podstawowa Kategoryczny Strunowy
Color1 Kolor 1 Kategoryczny Strunowy
Color2 Kolor 2 Kategoryczny Strunowy
MaturitySize Rozmiar w terminie dojrzałości Kategoryczny Strunowy
FurLength Długość futra Kategoryczny Strunowy
Vaccinated Zwierzę zostało zaszczepione Kategoryczny Strunowy
Sterilized Zwierzę zostało wysterylizowane Kategoryczny Strunowy
Health Stan zdrowia Kategoryczny Strunowy
Fee Opłata adopcyjna Liczbowy Liczba całkowita
Description Zapis profilu Tekst Strunowy
PhotoAmt Wszystkie przesłane zdjęcia Liczbowy Liczba całkowita
AdoptionSpeed Kategoryczna szybkość adopcji Klasyfikacja Liczba całkowita

Importuj TensorFlow i inne biblioteki

import numpy as np
import pandas as pd
import tensorflow as tf

from tensorflow.keras import layers
tf.__version__
'2.8.0-rc1'

Załaduj zbiór danych i wczytaj go do pandy DataFrame

pandas to biblioteka Pythona z wieloma pomocnymi narzędziami do ładowania i pracy z danymi strukturalnymi. Użyj tf.keras.utils.get_file , aby pobrać i wyodrębnić plik CSV z minizestawem danych PetFinder.my i załaduj go do DataFrame za pomocą pandas.read_csv :

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
1679360/1668792 [==============================] - 0s 0us/step

Sprawdź zestaw danych, sprawdzając pierwsze pięć wierszy DataFrame:

dataframe.head()

Utwórz zmienną docelową

Pierwotnym zadaniem w konkursie Kaggle PetFinder.my Adoption Prediction było przewidzenie szybkości, z jaką zwierzę zostanie adoptowane (np. w pierwszym tygodniu, pierwszym miesiącu, pierwszych trzech miesiącach i tak dalej).

W tym samouczku uprościsz zadanie, przekształcając je w binarny problem klasyfikacji, w którym musisz po prostu przewidzieć, czy zwierzę zostało adoptowane, czy nie.

Po zmodyfikowaniu kolumny AdoptionSpeed , 0 będzie oznaczać, że zwierzę nie zostało adoptowane, a 1 będzie oznaczać, że było.

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

# Drop unused features.
dataframe = dataframe.drop(columns=['AdoptionSpeed', 'Description'])

Podziel DataFrame na zestawy treningowe, walidacyjne i testowe

Zestaw danych znajduje się w pojedynczej pandzie DataFrame. Podziel go na zestawy treningowe, walidacyjne i testowe, stosując na przykład stosunek 80:10:10, odpowiednio:

train, val, test = np.split(dataframe.sample(frac=1), [int(0.8*len(dataframe)), int(0.9*len(dataframe))])
print(len(train), 'training examples')
print(len(val), 'validation examples')
print(len(test), 'test examples')
9229 training examples
1154 validation examples
1154 test examples

Utwórz potok wejściowy za pomocą tf.data

Następnie utwórz funkcję narzędziową, która konwertuje każdy zestaw danych uczących, walidacyjnych i testowych DataFrame na tf.data.Dataset , a następnie tasuje i grupuje dane.

def df_to_dataset(dataframe, shuffle=True, batch_size=32):
  df = dataframe.copy()
  labels = df.pop('target')
  df = {key: value[:,tf.newaxis] for key, value in dataframe.items()}
  ds = tf.data.Dataset.from_tensor_slices((dict(df), labels))
  if shuffle:
    ds = ds.shuffle(buffer_size=len(dataframe))
  ds = ds.batch(batch_size)
  ds = ds.prefetch(batch_size)
  return ds

Teraz użyj nowo utworzonej funkcji ( df_to_dataset ), aby sprawdzić format danych zwracanych przez funkcję pomocniczą potoku wejściowego, wywołując je na danych uczących, i użyj małego rozmiaru partii, aby dane wyjściowe były czytelne:

batch_size = 5
train_ds = df_to_dataset(train, batch_size=batch_size)
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:4: FutureWarning: Support for multi-dimensional indexing (e.g. `obj[:, None]`) is deprecated and will be removed in a future version.  Convert to a numpy array before indexing instead.
  after removing the cwd from sys.path.
[(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', 'target']
A batch of ages: tf.Tensor(
[[84]
 [ 1]
 [ 5]
 [ 1]
 [12]], shape=(5, 1), dtype=int64)
A batch of targets: tf.Tensor([1 1 0 1 0], shape=(5,), dtype=int64)

Jak pokazują dane wyjściowe, zestaw uczący zwraca słownik nazw kolumn (z DataFrame), które mapują wartości kolumn z wierszy.

Zastosuj warstwy przetwarzania wstępnego Keras

Warstwy przetwarzania wstępnego Keras umożliwiają tworzenie natywnych potoków przetwarzania danych wejściowych Keras, które mogą być używane jako niezależny kod przetwarzania wstępnego w przepływach pracy innych niż Keras, połączone bezpośrednio z modelami Keras i eksportowane jako część Keras SavedModel.

W tym samouczku użyjesz następujących czterech warstw przetwarzania wstępnego, aby zademonstrować, jak wykonać przetwarzanie wstępne, kodowanie danych strukturalnych i inżynierię funkcji:

Więcej informacji o dostępnych warstwach można znaleźć w przewodniku Praca z warstwami przetwarzania wstępnego .

  • W przypadku funkcji numerycznych zestawu danych PetFinder.my mini, użyjesz warstwy tf.keras.layers.Normalization , aby ujednolicić dystrybucję danych.
  • W przypadku funkcji kategorycznych , takich jak pet Type s (ciągi Dog i Cat ), przekształcisz je w tensory zakodowane na gorąco za pomocą tf.keras.layers.CategoryEncoding .

Kolumny numeryczne

Dla każdej funkcji numerycznej w minizestawie danych PetFinder.my użyjesz warstwy tf.keras.layers.Normalization , aby ujednolicić dystrybucję danych.

Zdefiniuj nową funkcję narzędziową, która zwraca warstwę, która stosuje normalizację funkcji pod kątem funkcji numerycznych przy użyciu tej warstwy przetwarzania wstępnego Keras:

def get_normalization_layer(name, dataset):
  # Create a Normalization layer for the feature.
  normalizer = layers.Normalization(axis=None)

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

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

  return normalizer

Następnie przetestuj nową funkcję, wywołując ją na wszystkich przesłanych funkcjach zdjęć zwierząt domowych, aby znormalizować 'PhotoAmt' :

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.8272058 ],
       [-0.19125296],
       [ 1.3986291 ],
       [-0.19125296],
       [-0.50922936]], dtype=float32)>

Kolumny kategorialne

Type zwierząt w zestawie danych są reprezentowane jako ciągi — Dog i Cat — które muszą być zakodowane w trybie multi-hot przed wprowadzeniem do modelu. Funkcja Age

Zdefiniuj kolejną nową funkcję użytkową, która zwraca warstwę, która odwzorowuje wartości ze słownika na indeksy liczb całkowitych i koduje obiekty w trybie multi-hot za pomocą przetwarzania wstępnego tf.keras.layers.StringLookup , tf.keras.layers.IntegerLookup i tf.keras.CategoryEncoding warstwy:

def get_category_encoding_layer(name, dataset, dtype, max_tokens=None):
  # Create a layer that turns strings into integer indices.
  if dtype == 'string':
    index = layers.StringLookup(max_tokens=max_tokens)
  # Otherwise, create a layer that turns integer values into integer indices.
  else:
    index = layers.IntegerLookup(max_tokens=max_tokens)

  # Prepare a `tf.data.Dataset` that only yields the 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)

  # Encode the integer indices.
  encoder = layers.CategoryEncoding(num_tokens=index.vocabulary_size())

  # Apply multi-hot encoding to the indices. The lambda function captures the
  # layer, so you can use them, or include them in the Keras Functional model later.
  return lambda feature: encoder(index(feature))

Przetestuj funkcję get_category_encoding_layer , wywołując ją na funkcjach 'Type' zwierzaka, aby przekształcić je w tensory zakodowane na gorąco:

test_type_col = train_features['Type']
test_type_layer = get_category_encoding_layer(name='Type',
                                              dataset=train_ds,
                                              dtype='string')
test_type_layer(test_type_col)
<tf.Tensor: shape=(5, 3), dtype=float32, numpy=
array([[0., 1., 0.],
       [0., 1., 0.],
       [0., 1., 0.],
       [0., 1., 0.],
       [0., 1., 0.]], dtype=float32)>

Powtórz ten proces dla funkcji 'Age' zwierzaka:

test_age_col = train_features['Age']
test_age_layer = get_category_encoding_layer(name='Age',
                                             dataset=train_ds,
                                             dtype='int64',
                                             max_tokens=5)
test_age_layer(test_age_col)
<tf.Tensor: shape=(5, 5), dtype=float32, numpy=
array([[1., 0., 0., 0., 0.],
       [0., 0., 0., 1., 0.],
       [1., 0., 0., 0., 0.],
       [0., 0., 0., 1., 0.],
       [1., 0., 0., 0., 0.]], dtype=float32)>

Wstępnie przetwórz wybrane funkcje, aby trenować model

Nauczyłeś się korzystać z kilku rodzajów warstw przetwarzania wstępnego Keras. Następnie będziesz:

  • Zastosuj zdefiniowane wcześniej funkcje narzędziowe do przetwarzania wstępnego na 13 numerycznych i kategorycznych funkcjach z mini zbioru danych PetFinder.my.
  • Dodaj wszystkie dane wejściowe funkcji do listy.

Jak wspomniano na początku, aby wytrenować model, użyjesz danych liczbowych ( 'PhotoAmt' , 'Fee' ) i kategorycznych ( 'Age' , 'Type' , 'Color1' , 'Color2' , 'Gender' ) PetFinder.my mini zbioru danych 'Gender' , 'MaturitySize' , 'FurLength' , 'Vaccinated' , 'Breed1' , 'Health' , 'Sterilized' ).

Wcześniej użyłeś małego rozmiaru partii do zademonstrowania potoku wejściowego. Utwórzmy teraz nowy potok wejściowy z większym rozmiarem partii 256:

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)
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:4: FutureWarning: Support for multi-dimensional indexing (e.g. `obj[:, None]`) is deprecated and will be removed in a future version.  Convert to a numpy array before indexing instead.
  after removing the cwd from sys.path.

Znormalizuj cechy liczbowe (liczbę zdjęć zwierząt domowych i opłatę adopcyjną) i dodaj je do jednej listy danych wejściowych o nazwie encoded_features :

all_inputs = []
encoded_features = []

# Numerical 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)

Zmień kategoryczne wartości liczb całkowitych ze zbioru danych (wiek zwierząt) na indeksy liczb całkowitych, wykonaj kodowanie multi-hot i dodaj wynikowe dane wejściowe funkcji do encoded_features :

age_col = tf.keras.Input(shape=(1,), name='Age', dtype='int64')

encoding_layer = get_category_encoding_layer(name='Age',
                                             dataset=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)

Powtórz ten sam krok dla wartości kategorycznych ciągu:

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(name=header,
                                               dataset=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)

Twórz, kompiluj i trenuj model

Następnym krokiem jest stworzenie modelu za pomocą Keras Functional API . W przypadku pierwszej warstwy w modelu połącz listę danych wejściowych encoded_features — w jeden wektor poprzez połączenie z tf.keras.layers.concatenate .

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)

Skonfiguruj model za pomocą Keras Model.compile :

model.compile(optimizer='adam',
              loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=["accuracy"])

Zwizualizujmy wykres łączności:

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

png

Następnie przeszkol i przetestuj model:

model.fit(train_ds, epochs=10, validation_data=val_ds)
Epoch 1/10
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/engine/functional.py:559: UserWarning: Input dict contained keys ['target'] which did not match any model input. They will be ignored by the model.
  inputs = self._flatten_to_reference_inputs(inputs)
37/37 [==============================] - 2s 19ms/step - loss: 0.6524 - accuracy: 0.5034 - val_loss: 0.5887 - val_accuracy: 0.6941
Epoch 2/10
37/37 [==============================] - 0s 8ms/step - loss: 0.5906 - accuracy: 0.6648 - val_loss: 0.5627 - val_accuracy: 0.7218
Epoch 3/10
37/37 [==============================] - 0s 8ms/step - loss: 0.5697 - accuracy: 0.6924 - val_loss: 0.5463 - val_accuracy: 0.7504
Epoch 4/10
37/37 [==============================] - 0s 8ms/step - loss: 0.5558 - accuracy: 0.6978 - val_loss: 0.5346 - val_accuracy: 0.7504
Epoch 5/10
37/37 [==============================] - 0s 8ms/step - loss: 0.5502 - accuracy: 0.7105 - val_loss: 0.5272 - val_accuracy: 0.7487
Epoch 6/10
37/37 [==============================] - 0s 8ms/step - loss: 0.5415 - accuracy: 0.7123 - val_loss: 0.5210 - val_accuracy: 0.7608
Epoch 7/10
37/37 [==============================] - 0s 8ms/step - loss: 0.5354 - accuracy: 0.7171 - val_loss: 0.5152 - val_accuracy: 0.7435
Epoch 8/10
37/37 [==============================] - 0s 8ms/step - loss: 0.5301 - accuracy: 0.7214 - val_loss: 0.5113 - val_accuracy: 0.7513
Epoch 9/10
37/37 [==============================] - 0s 8ms/step - loss: 0.5286 - accuracy: 0.7189 - val_loss: 0.5087 - val_accuracy: 0.7574
Epoch 10/10
37/37 [==============================] - 0s 8ms/step - loss: 0.5252 - accuracy: 0.7260 - val_loss: 0.5058 - val_accuracy: 0.7539
<keras.callbacks.History at 0x7f5f9fa91c50>
loss, accuracy = model.evaluate(test_ds)
print("Accuracy", accuracy)
5/5 [==============================] - 0s 6ms/step - loss: 0.5012 - accuracy: 0.7626
Accuracy 0.762565016746521

Wykonaj wnioskowanie

Opracowany model może teraz klasyfikować wiersz z pliku CSV bezpośrednio po dołączeniu warstw przetwarzania wstępnego do samego modelu.

Możesz teraz zapisać i ponownie wczytać model Keras za pomocą Model.save i Model.load_model przed wykonaniem wnioskowania na nowych danych:

model.save('my_pet_classifier')
reloaded_model = tf.keras.models.load_model('my_pet_classifier')
2022-01-26 06:20:08.013613: W tensorflow/python/util/util.cc:368] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
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

Aby uzyskać prognozę dla nowej próbki, wystarczy wywołać metodę Keras Model.predict . Musisz zrobić tylko dwie rzeczy:

  1. Zawiń skalary w listę, aby mieć wymiar partii ( Model przetwarza tylko partie danych, a nie pojedyncze próbki).
  2. Wywołaj tf.convert_to_tensor dla każdej funkcji.
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 77.7 percent probability of getting adopted.

Następne kroki

Aby dowiedzieć się więcej o klasyfikowaniu uporządkowanych danych, spróbuj pracować z innymi zbiorami danych. Aby poprawić dokładność podczas uczenia i testowania modeli, zastanów się dokładnie, które funkcje należy uwzględnić w modelu i jak powinny być reprezentowane.

Poniżej znajduje się kilka sugestii dotyczących zbiorów danych: