Встроенные представления для нескольких категориальных признаков с различной кардинальностью

Вопрос или проблема

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

Пример: 5 входных признаков:
C1, 10000 уникальных категорий
C2, 10 уникальных категорий
C3, 5000 уникальных категорий
C4, 15000 уникальных категорий
C5, 100 уникальных категорий

Опция 1:
Объединение признаков в одну матрицу эмбеддингов, в результате у меня будет единый словарь размером V=30110.

Опция 2:
Рассматривая каждый признак как отдельный словарь, у меня будет 5 отдельных и меньших матриц эмбеддингов, с V=10000,10,5000,15000,100, каждая из которых будет иметь своё количество измерений эмбеддингов. В целом меньше параметров для обучения, чем в Опции 1.

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

Что обычно делают на практике для такой проблемы? Большинство статей, которые я видел, упоминают об этом вскользь и просто представляют “слой эмбеддингов”.

Как будут реализованы отдельные матрицы эмбеддингов в TF.keras или pytorch с использованием слоев Embedding?

Ответ или решение

Теория

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

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

Пример

Рассмотрим пример с пятью категориальными признаками:

  • C1, 10,000 уникальных категорий
  • C2, 10 уникальных категорий
  • C3, 5,000 уникальных категорий
  • C4, 15,000 уникальных категорий
  • C5, 100 уникальных категорий

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

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

Применение

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

Реализация в TF.keras

Для реализации в TensorFlow.keras можно использовать Embedding класс для каждого отдельного признака:

from tensorflow.keras.layers import Embedding, Input, Dense, Concatenate
from tensorflow.keras.models import Model

# Создаем входы для каждого признака
input_c1 = Input(shape=(1,))
input_c2 = Input(shape=(1,))
input_c3 = Input(shape=(1,))
input_c4 = Input(shape=(1,))
input_c5 = Input(shape=(1,))

# Создаем раздельные слои эмбеддинга
embedding_c1 = Embedding(input_dim=10000, output_dim=10)(input_c1)
embedding_c2 = Embedding(input_dim=10, output_dim=3)(input_c2)
embedding_c3 = Embedding(input_dim=5000, output_dim=8)(input_c3)
embedding_c4 = Embedding(input_dim=15000, output_dim=12)(input_c4)
embedding_c5 = Embedding(input_dim=100, output_dim=5)(input_c5)

# Объединяем все слои эмбеддинга
merged = Concatenate()([embedding_c1, embedding_c2, embedding_c3, embedding_c4, embedding_c5])

# Добавляем полносвязный слой
dense_out = Dense(128, activation='relu')(merged)
output = Dense(1, activation='sigmoid')(dense_out)

# Строим модель
model = Model(inputs=[input_c1, input_c2, input_c3, input_c4, input_c5], outputs=output)
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])

Реализация в PyTorch

Для PyTorch подход аналогичен, но с использование nn.Embedding:

import torch
import torch.nn as nn
import torch.nn.functional as F

class CategoricalModel(nn.Module):
    def __init__(self):
        super(CategoricalModel, self).__init__()
        self.embedding_c1 = nn.Embedding(10000, 10)
        self.embedding_c2 = nn.Embedding(10, 3)
        self.embedding_c3 = nn.Embedding(5000, 8)
        self.embedding_c4 = nn.Embedding(15000, 12)
        self.embedding_c5 = nn.Embedding(100, 5)
        self.fc1 = nn.Linear(10+3+8+12+5, 128)
        self.fc2 = nn.Linear(128, 1)

    def forward(self, x1, x2, x3, x4, x5):
        x1 = self.embedding_c1(x1)
        x2 = self.embedding_c2(x2)
        x3 = self.embedding_c3(x3)
        x4 = self.embedding_c4(x4)
        x5 = self.embedding_c5(x5)

        x = torch.cat([x1, x2, x3, x4, x5], dim=1)
        x = F.relu(self.fc1(x))
        x = torch.sigmoid(self.fc2(x))
        return x

model = CategoricalModel()

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

Оцените материал
Добавить комментарий

Капча загружается...