Calcular la distancia de Levenshtein rápidamente

24

Dada una enorme base de datos de palabras permitidas (ordenadas alfabéticamente) y una palabra, encuentre la palabra de la base de datos que esté más cerca de la palabra dada en términos de distancia de Levenshtein.

El enfoque ingenuo es, por supuesto, simplemente calcular la distancia levenshtein entre la palabra dada y todas las palabras en el diccionario (podemos hacer una búsqueda binaria en la base de datos antes de calcular las distancias).

Me pregunto si hay una solución más eficiente para este problema. Tal vez alguna heurística que nos permita reducir la cantidad de palabras a buscar, u optimizaciones al algoritmo de distancia levenshtein.

Enlaces a documentos sobre el tema de bienvenida.

Joshua Herman
fuente

Respuestas:

16

Lo que está preguntando es el problema de la búsqueda de vecino bajo la distancia de edición. No mencionaste si te interesan los resultados teóricos o la heurística, por lo que responderé lo primero.

La distancia de edición es algo desagradable para tratar de construir estructuras de búsqueda cercanas. El principal problema es que, como métrica, se comporta (más o menos) como otras métricas conocidas como con el propósito de reducir y aproximar la dimensionalidad. Hay una gran cantidad de trabajo para leer sobre este tema, y ​​su mejor fuente es el conjunto de documentos de Alex Andoni : siguiendo los indicadores hacia atrás (por ejemplo, de su artículo de FOCS 2010) obtendrá un buen conjunto de fuentes.1

Suresh Venkat
fuente
1
Todo lo que sé sobre los espacios métricos es de la semántica, así que una pregunta: ¿hay alguna inserción decente (por cualquier valor decente) de la métrica de Levenshtein en una ultramétrica? Por casualidad, eso podría dar lugar al algoritmo binary-tree-ish.
Neel Krishnaswami
No estoy completamente seguro. Sospecho que la respuesta es no en general, pero no tengo nada que señalar.
Suresh Venkat
El segundo artículo sobre boytsov.info/pubs es una buena encuesta de posibles soluciones para la búsqueda de vecinos cercanos bajo la distancia de edición de Levenshtein y Damereau-Levenshtein.
a3nm
@NeelKrishnaswami Una incrustación en una unidad ultramétrica tendría una distorsión de al menos donde es la longitud de la cadena. Esto se debe a un límite inferior de distorsión para incrustar en debido a Krauthgamer y Rabani , ya que la ultrametría se incrusta isométricamente en el espacio euclidiano, que se incrusta isométricamente en . Ω(logd)dL1L1
Sasho Nikolov
5

Si tiene una pequeña cantidad de ediciones incorrectas que va a tolerar, puede intentar usar un árbol de sufijos punteado . Descargo de responsabilidad: escribí ese documento, pero resuelve lo que quiere: tiene un alto costo de espacio en disco, pero las consultas son realmente rápidas.

En general, es mejor mirarlo al revés: tiene un índice de todas las palabras en el diccionario. Ahora, para una palabra de entrada w, si está en el diccionario, deténgase. De lo contrario, genere todas las variaciones a la distancia 1 y búsquelas. Si no están allí, busque variaciones a la distancia 2, y así sucesivamente ...

Hay varias mejoras a esta idea básica.

luispedro
fuente
1
Debería haber incluido un enlace a su archivo de investigación reproducible para el artículo .
Dan D.
4

O(mk+1σk)mσk

Jouni Sirén
fuente
4

Escribí una respuesta a una pregunta muy similar en cs.stackexchange.com ( /cs//a/2096/1490 ) y luego encontré esta pregunta. La respuesta allí es una búsqueda aproximada de vecino cercano en la distancia de edición (es decir, el algoritmo genera una cadena que está aproximadamente tan cerca de la cadena de consulta como el vecino más cercano de la cadena de consulta). Estoy publicando aquí ya que no encuentro ninguna de las referencias que di allí en las respuestas dadas aquí.

Sasho Nikolov
fuente
3

Creo que lo que quiere es el algoritmo de Wagner-Fischer: https://en.wikipedia.org/wiki/Wagner%E2%80%93Fischer_algorithm La idea clave es que, dado que el diccionario que está atravesando está ordenado, dos palabras consecutivas es muy probable que comparta un prefijo largo, por lo que no necesita actualizar toda la matriz para cada cálculo de distancia.

Björn Lindqvist
fuente
2

Puedes usar ¿Quiso decir?

Y luego encuentre la distancia de Levenshtein entre la respuesta devuelta por "Quiso decir" y la cadena de entrada usando la Programación dinámica.

Pratik Deoghare
fuente
No entiendo esta respuesta. La pregunta pregunta cómo se puede encontrar eficientemente una palabra en un diccionario grande con una distancia cercana de Levenshtein a una entrada dada, no sobre cómo calcular la distancia de Levenshtein o sobre la comparación con la salida de un corrector ortográfico de caja negra ...
Huck Bennett
@Huck Bennett: pensé que @Grigory Javadyan está creando Did you mean?funciones. Además, Did you mean?devuelve la palabra que está muy cerca de la entrada dada y lo hace de manera bastante eficiente. :)
Pratik Deoghare
Creo que sus ideas son buenas, pero parece que Grigory está pidiendo algo más profundo y más específico.
Huck Bennett
@Huck Bennett: ¡Sí, tienes razón! :)
Pratik Deoghare
-1

Una forma es entrenar un modelo de aprendizaje automático para mapear las palabras a vectores y mapear la distancia levenshtein a la distancia euclidiana. Luego puede construir un KDTree a partir de los vectores para el diccionario que desea usar. Creé un cuaderno jupyter que hace esto aquí: https://gist.github.com/MichaelSnowden/9b8b1e662c98c514d571f4d5c20c3a03

Según los comentarios de DW:

  1. procedimiento de entrenamiento = descenso de gradiente estocástico con gradientes adaptativos
  2. función de pérdida = error cuadrático medio entre la verdadera distancia de edición y la distancia euclidiana
  3. datos de entrenamiento = cadenas aleatorias entre 1 y 32 caracteres de largo (podría mejorarse con datos que coincidan con una distribución real de errores tipográficos comunes)
  4. Resultados cuantitativos: después de entrenar durante aproximadamente 150 épocas con un tamaño de lote de 2048 (tiempo de pared = aproximadamente un minuto), utilizando incrustaciones de palabras de 512 dimensiones, con una capa oculta, el error absoluto promedio entre la distancia de edición real y la distancia de edición prevista se encuentra alrededor de 0,75, lo que significa que la distancia de edición prevista es aproximadamente un carácter

Resumen de la estructura del modelo:

  1. Cree una incrustación aprendida para cada carácter, incluido el carácter nulo (que se usa más adelante para rellenar con texto derecho debajo del límite de caracteres)
  2. Rellene el lado derecho del texto con el carácter nulo hasta que esté en el límite de caracteres (32)
  3. Concatenar estas incrustaciones
  4. Ejecute las incrustaciones a través de una red neuronal de avance para producir una incrustación de palabras de menor dimensión (512 dimensiones)
  5. Haz esto para ambas palabras
  6. Encuentre la distancia euclidiana entre los vectores
  7. Establezca la pérdida como el error cuadrático medio entre la verdadera distancia de Levenshtein y la distancia euclidiana

Mis datos de entrenamiento son solo cadenas aleatorias, pero creo que los resultados realmente podrían mejorar si los datos de entrenamiento fueran pares (error tipográfico / palabra correcta). Terminé usando /usr/share/dict/wordsporque está comúnmente disponible.

michaelsnowden
fuente
2
¿Cómo se entrena un modelo ML para que las palabras cercanas en Levenshtein se correspondan con vectores similares? ¿Qué procedimiento de entrenamiento y función de pérdida utilizas para eso? ¿Puede resumir el método en su respuesta, para que la respuesta siga siendo útil incluso si el enlace deja de funcionar, y para que no tengamos que buscar en su cuaderno para comprender el método que está utilizando? Además, ¿puede evaluar qué tan bien funciona de alguna manera cuantitativa? ¿Es esto mejor que las alternativas?
DW
Tal como está, esto es (creo) una mala elección para CSTheory. Es decir, ni idea de lo que se sugiere específicamente, ni justificación teórica para ello.
Clemente C.
@DW Lo siento, hice una edición bastante sustancial que debería ser exhaustiva en caso de que el enlace se caiga (o en caso de que no quiera hurgar en el cuaderno). Aunque esto no es realmente la teoría de CS porque no es investigación, creo que es un enfoque práctico porque es rápido y fácil tanto para el entrenamiento como para la inferencia.
michaelsnowden
1
Estás entrenando en cuerdas aleatorias. La distancia esperada de Levenshtein entre dos de estas cadenas será aproximadamente la longitud de la cadena más larga. Por lo tanto, es muy fácil estimar esta distancia en cadenas aleatorias, pero eso no es útil para tratar con datos del mundo real. Sospecho que sus incrustaciones podrían codificar la longitud de la cadena y, por lo tanto, es posible que haya creado una forma elegante de hacer algo trivial e inútil. Este es un problema con el uso de ML; Es muy sensible a la función de pérdida que utiliza.
DW
@DW Si observa los resultados en el cuaderno, la recuperación terminó devolviendo resultados decentes, no solo cadenas de la misma longitud. Realmente te animo a que lo leas. No lo llamaría trivial e inútil.
michaelsnowden