Consultas rápidas de distancia de hamming en postgres

15

Tengo una gran base de datos (16 millones de filas) que contiene hashes perceptuales de imágenes.

Me gustaría poder buscar filas por distancia de distancia en un plazo razonable.

Actualmente, hasta donde entiendo correctamente el problema, creo que la mejor opción aquí sería una implementación personalizada de SP-GiST que implemente un BK-Tree , pero eso parece mucho trabajo, y todavía estoy confuso en la práctica detalles de la implementación adecuada de un índice personalizado. El cálculo de la distancia de Hamming es lo suficientemente manejable, y hacer saber C, sin embargo.

Básicamente, ¿cuál es el enfoque apropiado aquí? Necesito poder buscar coincidencias dentro de una cierta distancia de edición de un hash. Según tengo entendido, la distancia de Levenshtein con cadenas de igual longitud es funcionalmente la distancia de Hamming, por lo que hay al menos algún soporte existente para lo que quiero, aunque no hay una forma clara de crear un índice a partir de él (recuerde, el valor que estoy buscando cambios. No puedo calcular previamente la distancia desde un valor fijo, ya que eso solo sería útil para ese valor).

Los hashes se almacenan actualmente como una cadena de 64 caracteres que contiene la codificación binaria ASCII del hash (por ejemplo, "10010101 ..."), pero puedo convertirlos a int64 con bastante facilidad. El verdadero problema es que necesito poder consultar relativamente rápido.

Parece que podría ser posible lograr algo similar a lo que quiero con el pg_trgm, pero no tengo claro cómo funciona el mecanismo de coincidencia de trigrama (en particular, ¿qué representa realmente la métrica de similitud que devuelve ? Parece algo así como editar-distancia).

El rendimiento de la inserción no es crítico (es muy costoso desde el punto de vista computacional calcular los hashes para cada fila), por lo que principalmente me preocupa la búsqueda.

Nombre falso
fuente
La extensión smlar puede tener lo que necesita: pgcon.org/2012/schedule/attachments/252_smlar-2012.pdf o pg_similarity: pgcon.org/2009/schedule/attachments/108_pg_similarity.pdf
Neil McGuigan
@NeilMcGuigan - ¡Interesante! La primera presentación es en realidad de las personas que mantienen los sistemas SP-GiST y GIST en postgres.
Nombre falso
Sin embargo, el primer enlace es para algo fundamentalmente diferente. están buscando intersecciones establecidas, mientras que yo estoy buscando distancia de hamming. Podría financiar las fases en un conjunto, pero sería extremadamente complicado y requeriría mucho código de soporte en cualquier otro lugar.
Nombre falso el
FWIW, en este punto, más o menos he concluido que necesito implementar mi propio sistema de indexación. Estoy buscando índices SP-GiST personalizados en este momento, pero no tengo idea de lo que estoy haciendo.
Nombre falso
1
@FakeName: cuando dices la distancia de hamming, supongo que te refieres a la distancia de hamming de las cadenas de valores hash, no de las imágenes. En otras palabras, está buscando preguntar: Encuentre todos los valores hash que son sustituciones de X bits lejos del parámetro de entrada
Thomas Kejser

Respuestas:

11

Bueno, pasé un tiempo mirando escribir una extensión C personalizada de postgres, y terminé simplemente escribiendo un contenedor de base de datos Cython que mantiene una estructura de árbol BK en la memoria.

Básicamente, mantiene una copia en memoria de los valores phash de la base de datos, y todas las actualizaciones de la base de datos se reproducen en el árbol BK.

Todo está en Github aquí . También tiene MUCHAS pruebas unitarias.

La consulta a través de un conjunto de datos de 10 millones de valores hash para elementos con una distancia de 4 resulta en tocar ~ 0.25% -0.5% de los valores en el árbol, y toma ~ 100 ms.

Nombre falso
fuente
BK-Tree en memoria con 16 millones de filas en memoria? Estaba mirando algo similar, sin embargo, con 1000 imágenes y 2000 descriptores en cada imagen, mi tamaño de memoria era enorme.
Stewart el
@Stewart: mucho de esto depende del tamaño de tu hash. En mi caso, la salida del valor hash es un único campo de bits de 64 bits que almaceno como int64. Parece que tiene un tipo de datos phash mucho más grande. Tampoco estoy seguro de cómo funcionarían las búsquedas en un tipo de datos diferente como ese. ¿Siguen siendo un espacio métrico? ¿Cómo se calcula la distancia?
Nombre falso el
Estoy usando descriptores de 32 bits con el marcador FLANN provisto con opencv. Para calcular la distancia, utilizo hamming con un umbral basado en la relación de Lowe's. En este punto, no estoy seguro de si es mejor tratar de mantener la memoria FLANN que proporciona una estructura de árbol KD o cambiar a una solución más similar a la suya. ¿Por qué terminaste rodando el tuyo y no buscas algo como libflann?
Stewart
@ Stewart - No hice el mío. Estoy usando un hashing súper aburrido basado en DFT .
Nombre falso el
7

¡RESPUESTAS DE MOAR!

Ok, finalmente me he tomado el tiempo para escribir una extensión de indexación PostgreSQL personalizada. He utilizado la interfaz de SP-GiST .

Esto fue bastante desafiante, principalmente porque Posgres es grande .

De todos modos, como de costumbre, está en github aquí .

En cuanto al rendimiento, actualmente es ~ 2-3 veces más lento que la implementación de memoria pura en mi otra respuesta a esta pregunta, pero es mucho más conveniente de usar. Me encantaría comer ese golpe de rendimiento (en realidad, es ~ 50 ms / query - 150 ms / query, que todavía es bastante pequeño).

Nombre falso
fuente
¡Usted es maravilloso! ¿Puedes agregar un README sobre cómo instalar? Realmente nunca instalé nada en Postgres: P
HypeWolf
1
@HypeWolf: la raíz del repositorio tiene un archivo README . ¿Eso no cubre lo que quieres?
Nombre falso el
Mi error, no lo vi, no estoy seguro de dónde estaba buscando: /
HypeWolf
Estaba buscando el README también. Está en la carpeta raíz. El enlace va a alguna subcarpeta. Eso fue confuso.
luckydonald