Классифицируйте структурированные данные с помощью слоев предварительной обработки Keras.

Посмотреть на TensorFlow.org Запустить в Google Colab Посмотреть исходный код на GitHub Скачать блокнот

В этом руководстве показано, как классифицировать структурированные данные, такие как табличные данные, с помощью упрощенной версии набора данных PetFinder из конкурса Kaggle, хранящегося в файле CSV.

Вы будете использовать Keras для определения модели и слоев предварительной обработки Keras в качестве моста для сопоставления столбцов в файле CSV с функциями, используемыми для обучения модели. Цель состоит в том, чтобы предсказать, будет ли домашнее животное усыновлено.

Этот учебник содержит полный код для:

  • Загрузка CSV-файла в DataFrame с помощью pandas .
  • Создание входного конвейера для пакетной обработки и перемешивания строк с использованием tf.data . (Посетите tf.data: Сборка входных конвейеров TensorFlow для более подробной информации.)
  • Сопоставление столбцов в файле CSV с функциями, используемыми для обучения модели с помощью слоев предварительной обработки Keras.
  • Создание, обучение и оценка модели с использованием встроенных методов Keras.

Мини-набор данных PetFinder.my

В CSV-файле набора данных PetFinder.my mini есть несколько тысяч строк, где каждая строка описывает домашнее животное (собаку или кошку), а каждый столбец описывает атрибут, такой как возраст, порода, цвет и т. д.

Обратите внимание, что в приведенной ниже сводке набора данных в основном представлены числовые и категориальные столбцы. В этом руководстве вы будете иметь дело только с этими двумя типами функций, отбрасывая Description (функция произвольного текста) и AdoptionSpeed (функция классификации) во время предварительной обработки данных.

Столбец Описание питомца Тип функции Тип данных
Type Тип животного ( Dog , Cat ) Категориальный Нить
Age Возраст Числовой Целое число
Breed1 Основная порода Категориальный Нить
Color1 Цвет 1 Категориальный Нить
Color2 Цвет 2 Категориальный Нить
MaturitySize Размер при погашении Категориальный Нить
FurLength Длина меха Категориальный Нить
Vaccinated Животное было вакцинировано Категориальный Нить
Sterilized животное было стерилизовано Категориальный Нить
Health Состояние здоровья Категориальный Нить
Fee Плата за усыновление Числовой Целое число
Description Написание профиля Текст Нить
PhotoAmt Всего загруженных фотографий Числовой Целое число
AdoptionSpeed Категориальная скорость принятия Классификация Целое число

Импорт TensorFlow и других библиотек

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

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

Загрузите набор данных и прочитайте его в pandas DataFrame.

pandas — это библиотека Python с множеством полезных утилит для загрузки и работы со структурированными данными. Используйте tf.keras.utils.get_file для загрузки и извлечения CSV-файла с мини-набором данных PetFinder.my и загрузки его в DataFrame с помощью 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

Проверьте набор данных, проверив первые пять строк DataFrame:

dataframe.head()

Создайте целевую переменную

Первоначальная задача конкурса Kaggle PetFinder.my Adoption Prediction состояла в том, чтобы предсказать скорость, с которой домашнее животное будет усыновлено (например, в первую неделю, первый месяц, первые три месяца и т. д.).

В этом уроке вы упростите задачу, превратив ее в проблему бинарной классификации, где вам просто нужно предсказать, было ли домашнее животное усыновлено или нет.

После изменения столбца AdoptionSpeed 0 будет означать, что питомец не был усыновлен, а 1 — что да.

# 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'])

Разделите DataFrame на наборы для обучения, проверки и тестирования.

Набор данных находится в одном кадре данных pandas. Разделите его на наборы для обучения, проверки и тестирования, используя, например, соотношение 80:10:10 соответственно:

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

Создайте входной конвейер, используя tf.data

Затем создайте служебную функцию, которая преобразует каждый обучающий, проверочный и тестовый набор данных DataFrame в tf.data.Dataset , а затем перемешивает и группирует данные.

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

Теперь используйте только что созданную функцию ( df_to_dataset ), чтобы проверить формат данных, которые возвращает вспомогательная функция входного конвейера, вызвав ее для обучающих данных, и используйте небольшой размер пакета, чтобы выходные данные оставались читаемыми:

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)

Как видно из выходных данных, обучающий набор возвращает словарь имен столбцов (из DataFrame), которые сопоставляются со значениями столбцов из строк.

Примените слои предварительной обработки Keras

Уровни предварительной обработки Keras позволяют создавать собственные конвейеры обработки ввода Keras, которые можно использовать в качестве независимого кода предварительной обработки в рабочих процессах, отличных от Keras, напрямую объединять с моделями Keras и экспортировать как часть Keras SavedModel.

В этом руководстве вы будете использовать следующие четыре уровня предварительной обработки, чтобы продемонстрировать, как выполнять предварительную обработку, кодирование структурированных данных и разработку функций:

  • tf.keras.layers.Normalization : выполняет поэлементную нормализацию входных объектов.
  • tf.keras.layers.CategoryEncoding : превращает целочисленные категориальные признаки в однократные, множественные или плотные представления tf-idf .
  • tf.keras.layers.StringLookup : превращает строковые категориальные значения в целые индексы.
  • tf.keras.layers.IntegerLookup : превращает целочисленные категориальные значения в целочисленные индексы.

Вы можете узнать больше о доступных слоях в руководстве Работа со слоями предварительной обработки .

  • Для числовых характеристик мини-набора данных PetFinder.my вы будете использовать слой tf.keras.layers.Normalization для стандартизации распределения данных.
  • Для категориальных функций , таких как pet Type (строки Dog и Cat ), вы преобразуете их в тензоры с горячим кодированием с помощью tf.keras.layers.CategoryEncoding .

Числовые столбцы

Для каждого числового признака в мини-наборе данных PetFinder.my вы будете использовать слой tf.keras.layers.Normalization для стандартизации распределения данных.

Определите новую служебную функцию, которая возвращает слой, который применяет поэлементную нормализацию к числовым функциям, используя этот слой предварительной обработки 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

Затем протестируйте новую функцию, вызвав ее на всех загруженных функциях фотографий домашних животных, чтобы нормализовать '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)>

Категориальные столбцы

Type домашних животных в наборе данных представлены в виде строк — Dog и Cat — которые должны быть многократно закодированы перед тем, как быть загруженными в модель. Функция Age

Определите еще одну новую служебную функцию, которая возвращает слой, который сопоставляет значения из словаря с целочисленными индексами и выполняет многократное горячее кодирование функций с использованием предварительной tf.keras.layers.StringLookup , tf.keras.layers.IntegerLookup и tf.keras.CategoryEncoding слои:

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

Протестируйте функцию get_category_encoding_layer , вызвав ее на функциях Pet 'Type' , чтобы превратить их в тензоры с множественным горячим кодированием:

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

Повторите процесс для питомца 'Age' :

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

Предварительно обработайте выбранные функции для обучения модели.

Вы узнали, как использовать несколько типов слоев предварительной обработки Keras. Далее вы будете:

  • Примените служебные функции предварительной обработки, определенные ранее, к 13 числовым и категориальным функциям из мини-набора данных PetFinder.my.
  • Добавьте все входные данные функции в список.

Как упоминалось в начале, для обучения модели вы будете использовать числовой ( 'PhotoAmt' , 'Fee' ) и категориальный ( 'Age' , 'Type' , 'Color1' Цвет1», 'Color2' Цвет2», 'Gender' Цвет» мини-набора данных PetFinder.my). 'Gender' , «Размер 'MaturitySize' , 'FurLength' , 'Vaccinated' , 'Sterilized' , 'Health' , 'Breed1' ).

Ранее вы использовали небольшой пакет для демонстрации входного конвейера. Давайте теперь создадим новый входной конвейер с большим размером пакета 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.

Нормализуйте числовые характеристики (количество фотографий домашних животных и плату за усыновление) и добавьте их в один список входных данных с именем 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)

Превратите целочисленные категориальные значения из набора данных (возраст питомца) в целочисленные индексы, выполните многократное горячее кодирование и добавьте полученные входные данные функций в 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)

Повторите тот же шаг для строковых категориальных значений:

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)

Создание, компиляция и обучение модели

Следующим шагом будет создание модели с помощью Keras Functional API . Для первого слоя в вашей модели объедините список входных признаков — encoded_features — в один вектор посредством конкатенации с помощью 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)

Настройте модель с помощью Model.compile :

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

Давайте визуализируем граф связности:

# Use `rankdir='LR'` 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
/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

Выполнить вывод

Разработанная вами модель теперь может классифицировать строку из CSV-файла сразу после того, как вы включили слои предварительной обработки в саму модель.

Теперь вы можете сохранить и перезагрузить модель Keras с помощью Model.save и Model.load_model перед выполнением вывода на новых данных:

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

Чтобы получить прогноз для нового образца, вы можете просто вызвать метод Model.predict . Вам нужно сделать всего две вещи:

  1. Оберните скаляры в список, чтобы иметь пакетное измерение ( Model обрабатывает только пакеты данных, а не отдельные образцы).
  2. Вызовите tf.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 77.7 percent probability of getting adopted.

Следующие шаги

Чтобы узнать больше о классификации структурированных данных, попробуйте поработать с другими наборами данных. Чтобы повысить точность во время обучения и тестирования ваших моделей, тщательно продумайте, какие функции включить в вашу модель и как они должны быть представлены.

Ниже приведены некоторые предложения для наборов данных:

  • Наборы данных TensorFlow: MovieLens : набор рейтингов фильмов из службы рекомендаций фильмов.
  • Наборы данных TensorFlow: Качество вина : два набора данных, относящиеся к красному и белому вариантам португальского вина «Виньо Верде». Вы также можете найти набор данных Red Wine Quality на Kaggle .
  • Kaggle: набор данных arXiv : сборник из 1,7 миллиона научных статей из arXiv, охватывающих физику, информатику, математику, статистику, электротехнику, количественную биологию и экономику.