Как добавить новые вложения для неизвестных слов в Tensorflow (обучение и предварительная настройка для тестирования)
Мне любопытно, как я могу добавить нормально-рандомизированный 300-мерный вектор (elements' type = tf.float32) всякий раз, когда встречается слово, неизвестное предварительно обученному словарю. Я использую предварительно обученные вложения слов GloVe, но в некоторых случаях я понимаю, что сталкиваюсь с неизвестными словами, и я хочу создать вектор слов с нормальной случайной выборкой для этого нового найденного неизвестного слова.
Проблема в том, что с моей текущей настройкой я использую https://www.tensorflow.org/api_docs/python/tf/contrib/lookup/index_table_from_tensor для преобразования слов в целые числа на основе известного словаря. Эта функция может создавать новые токены и хэшировать их для некоторого предопределенного количества слов из словарного запаса, но мой embed
не будет содержать вложение для этого нового неизвестного хеш-значения. Я не уверен, смогу ли я просто добавить рандомизированное вложение в конец embed
список.
Я также хотел бы сделать это эффективным способом, поэтому предварительно созданная функция или метод тензорного потока, вероятно, будут наиболее эффективными. Я определяю заранее известные специальные токены, такие как токен конца предложения и неизвестный по умолчанию, как пустую строку ("с индексом 0), но это ограничено в способности изучать различные неизвестные слова. В настоящее время я использую tf.nn.embedding_lookup() как последний шаг встраивания.
Я хотел бы иметь возможность добавлять новые случайные 300 d-векторы для каждого неизвестного слова в обучающих данных, и я также хотел бы добавить заранее подготовленные случайные векторы-слова для любых неизвестных токенов, не обнаруженных в обучении, которые, возможно, встречаются во время тестирования. Каков наиболее эффективный способ сделать это?
def embed_tensor(string_tensor, trainable=True):
"""
Convert List of strings into list of indicies then into 300d vectors
"""
# ordered lists of vocab and corresponding (by index) 300d vector
vocab, embed = load_pretrained_glove()
# Set up tensorflow look up from string word to unique integer
vocab_lookup = tf.contrib.lookup.index_table_from_tensor(
mapping=tf.constant(vocab),
default_value = 0)
string_tensor = vocab_lookup.lookup(string_tensor)
# define the word embedding
embedding_init = tf.Variable(tf.constant(np.asarray(embed),
dtype=tf.float32),
trainable=trainable,
name="embed_init")
# return the word embedded version of the sentence (300d vectors/word)
return tf.nn.embedding_lookup(embedding_init, string_tensor)
3 ответа
Пример кода ниже адаптирует ваш embed_tensor
Функция такова, что слова встраиваются следующим образом:
- Для слов, которые имеют предварительно обученное вложение, вложение инициализируется с помощью предварительно обученного вложения. Вложение может быть зафиксировано во время тренировки, если
trainable
являетсяFalse
, - Для слов в обучающих данных, которые не имеют предварительно обученного внедрения, вложение инициализируется случайным образом. Вложение может быть зафиксировано во время тренировки, если
trainable
являетсяFalse
, - Для слов в тестовых данных, которые не встречаются в обучающих данных и не имеют предварительно обученного внедрения, используется один произвольно инициализированный вектор внедрения. Этот вектор не может быть обучен.
import tensorflow as tf
import numpy as np
EMB_DIM = 300
def load_pretrained_glove():
return ["a", "cat", "sat", "on", "the", "mat"], np.random.rand(6, EMB_DIM)
def get_train_vocab():
return ["a", "dog", "sat", "on", "the", "mat"]
def embed_tensor(string_tensor, trainable=True):
"""
Convert List of strings into list of indices then into 300d vectors
"""
# ordered lists of vocab and corresponding (by index) 300d vector
pretrained_vocab, pretrained_embs = load_pretrained_glove()
train_vocab = get_train_vocab()
only_in_train = list(set(train_vocab) - set(pretrained_vocab))
vocab = pretrained_vocab + only_in_train
# Set up tensorflow look up from string word to unique integer
vocab_lookup = tf.contrib.lookup.index_table_from_tensor(
mapping=tf.constant(vocab),
default_value=len(vocab))
string_tensor = vocab_lookup.lookup(string_tensor)
# define the word embedding
pretrained_embs = tf.get_variable(
name="embs_pretrained",
initializer=tf.constant_initializer(np.asarray(pretrained_embs), dtype=tf.float32),
shape=pretrained_embs.shape,
trainable=trainable)
train_embeddings = tf.get_variable(
name="embs_only_in_train",
shape=[len(only_in_train), EMB_DIM],
initializer=tf.random_uniform_initializer(-0.04, 0.04),
trainable=trainable)
unk_embedding = tf.get_variable(
name="unk_embedding",
shape=[1, EMB_DIM],
initializer=tf.random_uniform_initializer(-0.04, 0.04),
trainable=False)
embeddings = tf.concat([pretrained_embs, train_embeddings, unk_embedding], axis=0)
return tf.nn.embedding_lookup(embeddings, string_tensor)
К вашему сведению, чтобы иметь разумное, неслучайное представление для слов, которые не встречаются в обучающих данных и не имеют предварительно подготовленного встраивания, вы можете рассмотреть возможность отображения слов с низкой частотой в ваших обучающих данных в токен unk (что не в вашем словаре) и сделать unk_embedding
обучаемая. Таким образом, вы узнаете прототип для слов, которые не видны в данных обучения.
Я никогда не пробовал, но я могу попытаться предложить возможный способ, используя те же механизмы вашего кода, но я подумаю об этом позже.
index_table_from_tensor
метод принимает num_oov_buckets
параметр, который перемешивает все ваши oov слова в заранее определенное количество сегментов.
Если вы установите для этого параметра определенное "достаточно большое" значение, вы увидите, как ваши данные распределяются по этим сегментам (каждый сегмент имеет идентификатор> идентификатор последнего словарного слова).
Так,
- если (при каждом поиске) вы устанавливаете (т.е.
assign
) последние строки (те, которые соответствуют ведрам) вашегоembedding_init
Переменная на случайное значение - если вы сделаете
num_oov_buckets
достаточно большой, чтобы столкновения были сведены к минимуму
Вы можете получить поведение, которое (приблизительно) соответствует тому, что вы просите, очень эффективным способом.
Случайное поведение может быть оправдано теорией, аналогичной теории хеш-таблиц: если число сегментов достаточно велико, метод хеширования строк будет назначать каждое слово oov другому блоку с высокой вероятностью (т.е. минимизировать столкновения с одним и тем же ведра). Так как вы назначаете разные случайные числа для каждого отдельного сегмента, вы можете получить (почти) различное отображение каждого oov-слова.
Идея, которая у меня была для этого, заключалась в том, чтобы записать новые слова в предварительно обученное вложение, добавив новое измерение для каждого нового слова (в основном поддерживая их горячую природу).
Предполагая, что количество новых слов невелико, но они важны, вы можете, например, увеличить размеры встроенных результатов с 300 до 300 + # новых слов, где каждое новое слово получит все нули, кроме 1 в его измерении.