En el aprendizaje automático , el hash de características , también conocido como truco de hash (por analogía con el truco del núcleo ), es una forma rápida y que ahorra espacio de vectorizar características , es decir, convertir características arbitrarias en índices en un vector o matriz. [1] [2] Funciona aplicando una función hash a las características y usando sus valores hash como índices directamente (después de una operación de módulo), en lugar de buscar los índices en una matriz asociativa . Además de su uso para codificar valores no numéricos, el hash de características también se puede utilizar para la reducción de dimensionalidad . [2]
Este truco se atribuye a menudo a Weinberger et al. (2009), [2] pero existe una descripción mucho anterior de este método publicada por John Moody en 1989. [1]
En una tarea típica de clasificación de documentos , la entrada al algoritmo de aprendizaje automático (tanto durante el aprendizaje como durante la clasificación) es texto libre. A partir de esto, se construye una representación en bolsa de palabras (BOW): se extraen y cuentan los tokens individuales, y cada token distinto en el conjunto de entrenamiento define una característica (variable independiente) de cada uno de los documentos tanto en el conjunto de entrenamiento como en el de prueba.
Sin embargo, los algoritmos de aprendizaje automático suelen definirse en términos de vectores numéricos. Por lo tanto, las bolsas de palabras de un conjunto de documentos se consideran una matriz término-documento donde cada fila es un solo documento y cada columna es una sola característica/palabra; la entrada i , j en dicha matriz captura la frecuencia (o peso) del término j 'ésimo del vocabulario en el documento i . (Una convención alternativa intercambia las filas y columnas de la matriz, pero esta diferencia es irrelevante). Por lo general, estos vectores son extremadamente dispersos , de acuerdo con la ley de Zipf .
El enfoque común es construir, en el momento del aprendizaje o antes de él, una representación de diccionario del vocabulario del conjunto de entrenamiento y utilizarla para asignar palabras a índices. Las tablas hash y los intentos son candidatos comunes para la implementación de diccionarios. Por ejemplo, los tres documentos
se puede convertir, usando el diccionario
a la matriz término-documento
(Se eliminó la puntuación, como es habitual en la clasificación y agrupamiento de documentos).
El problema de este proceso es que estos diccionarios ocupan una gran cantidad de espacio de almacenamiento y aumentan de tamaño a medida que crece el conjunto de entrenamiento. [3] Por el contrario, si el vocabulario se mantiene fijo y no se incrementa con un conjunto de entrenamiento creciente, un adversario puede intentar inventar palabras nuevas o errores ortográficos que no estén en el vocabulario almacenado para eludir un filtro aprendido por máquina. Para abordar este desafío, Yahoo! Research intentó utilizar el hash de características para sus filtros de spam. [4]
Tenga en cuenta que el truco del hash no se limita a la clasificación de texto y tareas similares a nivel de documento, sino que se puede aplicar a cualquier problema que involucre grandes (quizás ilimitadas) cantidades de características.
Matemáticamente, un token es un elemento de un conjunto finito (o infinito contable) . Supongamos que solo necesitamos procesar un corpus finito, entonces podemos poner todos los tokens que aparecen en el corpus en , lo que significa que es finito. Sin embargo, supongamos que queremos procesar todas las palabras posibles formadas por las letras del inglés, entonces es infinito contable.
La mayoría de las redes neuronales sólo pueden operar con entradas vectoriales reales, por lo que debemos construir una función "diccionario" .
Cuando es finito, de tamaño , entonces podemos usar la codificación one-hot para mapearlo en . Primero, enumeramos arbitrariamente , luego definimos . En otras palabras, asignamos un índice único a cada token, luego mapeamos el token con índice al vector de base unitaria .
La codificación one-hot es fácil de interpretar, pero requiere que se mantenga la enumeración arbitraria de . Dado un token , para calcular , debemos encontrar el índice del token . Por lo tanto, para implementar de manera eficiente, necesitamos una biyección que sea rápida de calcular , entonces tenemos .
De hecho, podemos relajar un poco el requisito: basta con tener una inyección rápida de calcular y luego usar .
En la práctica, no existe una forma sencilla de construir una inyección eficiente . Sin embargo, no necesitamos una inyección estricta, sino solo una inyección aproximada . Es decir, cuando , probablemente deberíamos tener , de modo que probablemente .
En este punto, acabamos de especificar que debe ser una función hash. De esta manera llegamos a la idea del hash de características.
El algoritmo de hash de características básicas presentado en (Weinberger et al. 2009) [2] se define de la siguiente manera.
Primero, se especifican dos funciones hash: la hash de núcleo y la hash de signo . A continuación, se define la función hash de característica: Por último, se extiende esta función hash de característica a cadenas de tokens mediante donde es el conjunto de todas las cadenas finitas que constan de tokens en .
De manera equivalente,
Queremos decir algo sobre la propiedad geométrica de , pero , por sí mismo, es solo un conjunto de tokens, no podemos imponerle una estructura geométrica excepto la topología discreta, que se genera por la métrica discreta . Para hacerlo más agradable, lo elevamos a , y lo elevamos de a por extensión lineal: Hay una suma infinita allí, que debe manejarse de inmediato. Esencialmente, solo hay dos formas de manejar infinitos. Uno puede imponer una métrica, luego tomar su completitud , para permitir sumas infinitas de buen comportamiento, o uno puede exigir que nada sea realmente infinito, solo potencialmente infinito . Aquí, vamos por el camino potencial-infinito, restringiendo para contener solo vectores con soporte finito : , solo un número finito de entradas de son distintas de cero.
Definamos un producto interno de la manera obvia: como nota al margen, si es infinito, entonces el espacio del producto interno no está completo . Si tomamos su completitud, obtendremos un espacio de Hilbert , que permite sumas infinitas con buen comportamiento.
Ahora tenemos un espacio de producto interno, con suficiente estructura para describir la geometría de la función hash de características .
Primero, podemos ver por qué se llama " hash de kernel ": nos permite definir un kernel por En el lenguaje del "truco del kernel", es el kernel generado por el "mapa de características" Nótese que este no es el mapa de características que estábamos usando, que es . De hecho, hemos estado usando otro kernel , definido por El beneficio de aumentar el hash del kernel con el hash binario es el siguiente teorema, que establece que es una isometría "en promedio".
Teorema (enunciado intuitivamente) : si el hash binario es imparcial (lo que significa que toma valor con igual probabilidad), entonces es una isometría en expectativa:
Por linealidad de la expectativa, ahora, , ya que asumimos que es insesgado. Por lo tanto, continuamos
La afirmación y prueba anteriores interpretan la función hash binaria no como una función determinista de tipo , sino como un vector binario aleatorio con entradas imparciales, lo que significa que para cualquier .
Esta es una buena imagen intuitiva, aunque no rigurosa. Para una afirmación y una demostración rigurosas, véase [2]
En lugar de mantener un diccionario, un vectorizador de características que utiliza el truco del hash puede construir un vector de una longitud predefinida aplicando una función hash h a las características (por ejemplo, palabras), y luego utilizando los valores hash directamente como índices de características y actualizando el vector resultante en esos índices. Aquí, asumimos que característica en realidad significa vector de características.
función hashing_vectorizer ( características : matriz de cadenas , N : entero ) : x := nuevo vector [ N ] para f en características : h := hash ( f ) x [ h mod N ] += 1 devuelve x
Por lo tanto, si nuestro vector de características es ["gato", "perro", "gato"] y la función hash es si es "gato" y si es "perro". Supongamos que la dimensión del vector de características de salida ( N ) es 4. Entonces la salida x será [0,2,1,0]. Se ha sugerido que se utilice una segunda función hash de salida de un solo bit ξ para determinar el signo del valor de actualización, para contrarrestar el efecto de las colisiones hash . [2] Si se utiliza dicha función hash, el algoritmo se convierte en
función hashing_vectorizer ( características : matriz de cadenas , N : entero ) : x := nuevo vector [ N ] para f en características : h := hash ( f ) idx := h mod N si ξ ( f ) == 1 : x [ idx ] += 1 de lo contrario : x [ idx ] -= 1 devuelve x
El pseudocódigo anterior convierte cada muestra en un vector. Una versión optimizada solo generaría un flujo de pares y dejaría que los algoritmos de aprendizaje y predicción consumieran dichos flujos; entonces se puede implementar un modelo lineal como una única tabla hash que represente el vector de coeficientes.
El hash de características generalmente sufre colisión de hashes, lo que significa que existen pares de tokens diferentes con el mismo hash: . Un modelo de aprendizaje automático entrenado con palabras con hash de características tendría dificultades para distinguir y , esencialmente porque es polisémico .
Si es poco frecuente, la degradación del rendimiento es pequeña, ya que el modelo siempre podría ignorar el caso poco frecuente y hacer como si todos los medios fueran así . Sin embargo, si ambos son comunes, la degradación puede ser grave.
Para manejar esto, se pueden entrenar funciones hash supervisadas que eviten mapear tokens comunes a los mismos vectores de características. [5]
Ganchev y Dredze demostraron que en aplicaciones de clasificación de texto con funciones hash aleatorias y varias decenas de miles de columnas en los vectores de salida, el hash de características no necesita tener un efecto adverso en el rendimiento de la clasificación, incluso sin la función hash con signo. [3]
Weinberger et al. (2009) aplicaron su versión de hash de características al aprendizaje multitarea y, en particular, al filtrado de spam , donde las características de entrada son pares (usuario, característica) de modo que un único vector de parámetros capturó filtros de spam por usuario, así como un filtro global para varios cientos de miles de usuarios, y descubrieron que la precisión del filtro aumentó. [2]
Chen et al. (2015) combinaron la idea del hash de características y la matriz dispersa para construir "matrices virtuales": matrices grandes con pequeños requisitos de almacenamiento. La idea es tratar una matriz como un diccionario, con claves en y valores en . Luego, como es habitual en los diccionarios hash, se puede usar una función hash y, por lo tanto, representar una matriz como un vector en , sin importar cuán grande sea. Con matrices virtuales, construyeron HashedNets , que son grandes redes neuronales que solo requieren pequeñas cantidades de almacenamiento. [6]
Las implementaciones del truco de hash están presentes en:
Asigna una secuencia de términos a sus frecuencias de término utilizando el truco del hashing.
Convierte un texto en una secuencia de índices en un espacio de hash de tamaño fijo.