Cómo agregar nuevas incrustaciones para palabras desconocidas en Tensorflow (capacitación y ajuste previo para la prueba)

Tengo curiosidad en cuanto a cómo puedo agregar un vector de 300 dimensiones normal-aleatorizado (tipo de elementos = tf.float32) cuando se encuentra una palabra desconocida para el vocabulario pre-entrenado. Estoy utilizando incrustaciones de palabras GloVe pre-entrenadas, pero en algunos casos, me doy cuenta de que encuentro palabras desconocidas, y quiero crear un vector de palabras aleatorias normales para esta nueva palabra desconocida encontrada.

El problema es que con mi configuración actual, uso tf.contrib.lookup.index_table_from_tensor para convertir de palabras a enteros en función del vocabulario conocido. Esta función puede crear nuevos tokens y hacer un hash para un número predefinido de palabras sin vocabulario, pero mi embed no contendrá una incrustación para este nuevo valor de hash desconocido. No estoy seguro de si puedo simplemente agregar una inserción aleatoria al final de la lista de embed .

También me gustaría hacer esto de una manera eficiente, por lo que la función o el método de flujo tensor precomstackdo que involucra las funciones de flujo tensor sería probablemente el más eficiente. Defino tokens especiales pre-conocidos como un token de fin de oración y un valor predeterminado desconocido como la cadena vacía (“en el índice 0), pero esto tiene un poder limitado para aprender de varias palabras desconocidas diferentes. Actualmente uso tf.nn .embedding_lookup () como paso final de incrustación.

Me gustaría poder agregar nuevos vectores aleatorios 300d para cada palabra desconocida en los datos de entrenamiento, y también me gustaría agregar vectores de palabras aleatorios prefabricados para cualquier token desconocido que no se haya visto en el entrenamiento que posiblemente se encuentre durante la prueba. ¿Cuál es la forma más eficiente de hacer esto?

 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) 

El ejemplo de código a continuación adapta su función embed_tensor tal manera que las palabras se insertan de la siguiente manera:

  • Para las palabras que tienen una inserción pre-entrenada, la incrustación se inicializa con la inserción pre-entrenada. La incrustación puede mantenerse fija durante el entrenamiento si trainable es False .
  • Para las palabras en los datos de entrenamiento que no tienen una inserción pre-entrenada, la incrustación se inicializa al azar. La incrustación puede mantenerse fija durante el entrenamiento si trainable es False .
  • Para las palabras en los datos de prueba que no aparecen en los datos de entrenamiento y no tienen una inserción pre-entrenada, se usa un único vector de incrustación inicializado al azar. Este vector no puede ser entrenado.
 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) 

Para su información, para tener una representación sensata y no aleatoria de palabras que no aparecen en los datos de entrenamiento y no tienen una inserción pre-entrenada, podría considerar mapear palabras con una baja frecuencia en sus datos de entrenamiento a un token de unk (que no está en su vocabulario) y haga que la unk_embedding entrenable. De esta manera, aprenderá un prototipo de palabras que no se ven en los datos de entrenamiento.

Nunca lo intenté, pero puedo intentar proporcionar una forma posible utilizando los mismos mecanismos de su código, pero lo pensaré más adelante.

El método index_table_from_tensor acepta un parámetro num_oov_buckets que baraja todas las palabras de tu oov en un número predefinido de cubos.

Si establece este parámetro en un cierto valor ‘suficientemente grande’, verá que los datos se reparten entre estos grupos (cada grupo tiene un ID> ID de la última palabra en vocabulario).

Asi que,

  • si (en cada búsqueda) establece (es decir, assign ) las últimas filas (las correspondientes a los grupos) de su variable de embedding_init a un valor aleatorio
  • si haces num_oov_buckets suficientemente grande como para que se minimicen las colisiones

puede obtener un comportamiento que es (una aproximación de) lo que está preguntando de una manera muy eficiente.

El comportamiento aleatorio se puede justificar con una teoría similar a la de las tablas hash: si el número de grupos es suficientemente grande, el método de hashing de las cadenas asignará cada palabra oov a un grupo diferente con alta probabilidad (es decir, minimizar las colisiones a la misma). cubos). Dado que, está asignando un número aleatorio diferente a cada grupo diferente, puede obtener un (casi) mapeo diferente de cada palabra oov.

Una idea que tuve para esto fue capturar las nuevas palabras para la inserción pre-entrenada agregando una nueva dimensión para cada nueva palabra (básicamente manteniendo la naturaleza candente de ellas).

Suponiendo que el número de palabras nuevas es pequeño pero son importantes, por ejemplo, podría boost las dimensiones de sus resultados incrustados de 300 a 300 + # de palabras nuevas donde cada palabra nueva obtendría todos los ceros excepto 1 en su dimensión.