Как узнать представление вложений для категориальных переменных.

Кодирование категориальных переменных в машинном обучении

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

Двумя наиболее известными способами преобразования категориальных переменных являются LabelEncoding и One Hot Encoding. Первый преобразует строковые метки в k целочисленных значений. Второй создает k столбцов и устанавливает значение переменной как 1 или 0 в зависимости от категории, но существует большое разнообразие кодировок.

Проблема с LabelEncoding заключается в том, что иногда может навести естественный порядок в разных классах. Например, алгоритм считает, что класс 1 менее важен, чем класс 2 только потому, что 2 больше 1. С другой стороны, One Hot Encoding может создать слишком много столбцов, если у вас много разных классов, и некоторые алгоритмы не слишком хороши для этого.

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

Итак, что такое вложения?

Простое определение встраивания от сотрудников Tensorflow:

Встраивание - это отображение дискретных объектов, таких как слова, на векторы действительных чисел.

Отдельные измерения в этих векторах обычно не имеют внутреннего значения. Вместо этого машинное обучение использует общие закономерности расположения и расстояния между векторами.

Одним из способов создания встраивания является обучение (или использование предварительно обученной) модели, например word2vect. Например, если мы обучаем встраиванию из текстов и строим график результатов, мы можем получить такую ​​проекцию:

На изображении мы видим, что такие слова, как идеи, ближе к перспективе, чем история. Это основная идея изучения встраивания. Теперь давайте посмотрим, как мы можем изучить встраивание по нашим категориальным переменным.

Изучите вложения от типов покемонов

Для этого поста мы собираемся использовать набор данных Покемон со статистикой (просто потому, что это кажется забавной идеей) от Kaggle. Набор данных включает информацию о 800 покемонах, в том числе: имя, тип, HP, атаку и другую статистику. Включая Итого, который представляет собой сумму всех статистических данных.

Данные выглядят так:

Первый шаг - загрузить данные

pokemon = pd.read_csv("Pokemon.csv")
pokemon.drop(labels="#", axis=1, inplace=True)
pokemon.fillna(value="No Type", axis=1, inplace=True)
pokemon.rename({'Type 1': 'type'}, inplace=True, axis=1)

А теперь давайте проверим, сколько разных типов покемонов у нас есть.

n_types = pokemon['type'].nunique()
print("We have:",n_types, "diferents pokemons types")
We have: 18 diferents pokemons types

Итак, у нас есть 18 различных типов покемонов, это число важно, поэтому мы собираемся его сохранить.

Нам нужно использовать LabelEncoder для преобразования наших строковых значений в целое число и MinMaxScaler для масштабирования Total (будет нашей целевой переменной) в диапазоне [0,1], это поможет обучить нашу модель встраивания.

from sklearn.preprocessing import LabelEncoder, MinMaxScaler
encoder = LabelEncoder()
scaler = MinMaxScaler()
pokemon['encoded_type'] = encoder.fit_transform(pokemon['type'])
pokemon['scaled_total'] = scaler.fit_transform(pokemon[['Total']])
types = pokemon['encoded_type']
total = pokemon['scaled_total']

Теперь наши данные выглядят так:

Пришло время создать нашу модель встраивания, для этого мы будем использовать Keras. Первый шаг - определить размер встраивания. Джереми Ховард предлагает использовать следующую формулу, в нашем случае размер встраивания должен быть 9.

embedding_size = min(np.ceil((no_of_unique_cat)/2),50)

Я собираюсь использовать 3 как размер вложения. Это потому, что я хочу построить результаты без использования PCA или t-SNE. Итак, давайте определим нашу модель:

from keras.models import Sequential
from keras.layers import Dense, Embedding, Flatten
model = Sequential()
model.add(Embedding(input_dim=n_types,output_dim=emb_size, input_length=1, name="poke_embedding"))
model.add(Flatten())
model.add(Dense(30, activation="relu"))
model.add(Dense(15, activation="relu"))
model.add(Dense(1, activation="linear"))

Мы определяем уровень внедрения, где input_dim соответствует размеру нашего словаря (18), output_dim - это размер нашего вложения, а input_length равно 1, потому что мы собираемся использовать только 1 слово.

Чтобы скомпилировать модель, мы собираемся использовать Adam в качестве оптимизатора, и наша функция потерь будет mse, потому что переменная Total является непрерывной.

model.compile(optimizer=”adam”, loss=”mse”)

Мы должны тренировать модель до тех пор, пока loss не сойдется до стабильного значения, для этого я собираюсь тренировать ее в течение 30 эпох (может быть, слишком много).

model.fit(x=types.values, y=total.values, epochs=30)

После 9 эпох потери сходятся к 0,0367, а затем стабилизируются на этом значении. Мы можем получить weights слоя внедрения и сохранить их в DataFrame.

embedding_layer = model.get_layer(name="poke_embedding")
embedding_layer = pd.DataFrame(embedding_layer.get_weights()[0])
embedding_layer.columns = ['C1','C2','C3']

Построение результатов

Мы собираемся изобразить веса и посмотреть, как связаны различные вложения.

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d, Axes3D
types_names = list(encoder.inverse_transform([x for x in range(0,n_types)]))
xs = embedding_layer['C1']
ys = embedding_layer['C2']
zs = embedding_layer['C3']
fig = plt.figure(figsize=(8, 4))
ax = fig.add_subplot(111, projection='3d')
for index, embedding in embedding_layer.iterrows():
    x = embedding['C1']
    y = embedding['C2']
    z = embedding['C3']
    ax.scatter(x, y, z, color='b')
    ax.text(x, y, z, '%s' % (types[index]), size=9, zorder=1, color='k')
plt.draw()

Запустив предыдущий код, получаем:

С другой стороны:

На изображении видно, что некоторые типы ближе друг к другу, это может означать, что у этих покемонов (одного типа) могут быть похожие характеристики (я не эксперт по покемонам). Это дало нам векторное представление различных типов. Мы можем использовать этот вектор, чтобы заменить категориальную переменную в исходном наборе данных и обучить с ней некоторые алгоритмы машинного обучения.

Выводы

Использование вложений - отличный способ представить категориальные переменные и уменьшить размерность категорий. Следующим шагом (возможно, для другого поста) является использование этого векторного представления для обучения моделей машинного обучения и сравнения с другими методами категориального кодирования. В моих экспериментах использование вложений всегда дает лучшие результаты, но представление категорий делает модель менее интерпретируемой.

Я работаю над своим собственным пакетом, чтобы сделать это проще, вы можете проверить его здесь.