Schnelle Hamming-Distanz-Abfragen in Postgres

15

Ich habe eine große Datenbank (16 Millionen Zeilen) mit wahrnehmbaren Hashes von Bildern.

Ich möchte in der Lage sein, nach Zeilen zu suchen, indem ich die Entfernung in einem angemessenen Zeitrahmen einschränke.

Derzeit denke ich, dass, soweit ich das Problem richtig verstehe, die beste Option hier eine benutzerdefinierte SP-GiST-Implementierung ist, die einen BK-Tree implementiert , aber das scheint eine Menge Arbeit zu sein, und ich bin immer noch unklar im praktischen Bereich Details zur ordnungsgemäßen Implementierung eines benutzerdefinierten Index. Die Berechnung der Hamming - Distanz ist lenkbar genug, und ich tun weiß , C, though.

Was ist hier grundsätzlich der richtige Ansatz? Ich muss in der Lage sein, Übereinstimmungen innerhalb einer bestimmten Bearbeitungsentfernung eines Hashs abzufragen. So wie ich es verstehe, ist Levenshtein-Abstand mit Zeichenfolgen gleicher Länge funktional ein Hamming-Abstand. Es gibt also zumindest eine gewisse Unterstützung für das, was ich möchte, obwohl es keine eindeutige Möglichkeit gibt, daraus einen Index zu erstellen (denken Sie daran, den Wert, nach dem ich frage) Ich kann den Abstand zu einem festen Wert nicht vorberechnen, da dies nur für diesen einen Wert sinnvoll wäre.

Die Hashes werden derzeit als 64-Zeichen-Zeichenfolge gespeichert, die die binäre ASCII-Codierung des Hashs enthält (z. B. "10010101 ..."), aber ich kann sie problemlos in int64 konvertieren. Das eigentliche Problem ist, dass ich in der Lage sein muss, relativ schnell abzufragen.

Es scheint möglich zu sein, etwas in der Art zu erreichen, wie ich es möchte pg_trgm, aber ich bin mir ein wenig unklar, wie der Trigramm-Matching-Mechamismus funktioniert (insbesondere, welche Ähnlichkeitsmetrik gibt es tatsächlich wieder ? Es sieht aus Art wie Bearbeitungsentfernung).

Die Einfügeleistung ist nicht kritisch (es ist sehr rechenintensiv, die Hashes für jede Zeile zu berechnen), daher kümmere ich mich in erster Linie um die Suche.

Falscher Name
quelle
Die smlar Erweiterung haben könnte , was Sie brauchen: pgcon.org/2012/schedule/attachments/252_smlar-2012.pdf oder pg_similarity: pgcon.org/2009/schedule/attachments/108_pg_similarity.pdf
Neil McGuigan
@NeilMcGuigan - Interessant! Die erste Präsentation gibt es eigentlich von den Leuten, die die SP-GiST- und GIST-Systeme in postgres pflegen.
Fake Name
Das erste Glied ist jedoch etwas grundlegend anderes. Sie suchen nach festgelegten Kreuzungen, während ich nach Hamming-Distanz suche. Ich könnte die Phashes zu einem Set zusammenfassen, aber es wäre extrem chaotisch und würde überall viel Support-Code erfordern.
Fake Name
FWIW, An diesem Punkt bin ich mehr oder weniger zu dem Schluss gekommen, dass ich mein eigenes Indizierungssystem implementieren muss. Ich suche gerade nach benutzerdefinierten SP-GiST-Indizes, aber ich habe keine Ahnung, was ich tue.
Fake Name
1
@FakeName: Wenn Sie die Hamming-Distanz sagen, gehe ich davon aus, dass Sie die Hamming-Distanz der Hash-Wert-Strings meinen, nicht der Bilder? Mit anderen Worten, Sie
möchten

Antworten:

11

Nun, ich habe eine Weile damit verbracht, eine benutzerdefinierte Postgres-C-Erweiterung zu schreiben, und habe gerade einen Cython-Datenbank-Wrapper geschrieben, der eine BK-Baumstruktur im Speicher beibehält.

Grundsätzlich wird eine speicherinterne Kopie der Phash-Werte aus der Datenbank verwaltet, und alle Aktualisierungen der Datenbank werden im BK-Baum wiedergegeben.

Es ist alles auf Github hier . Es hat auch viele Unit-Tests.

Das Abfragen eines Datensatzes mit 10 Millionen Hashwerten für Elemente mit einem Abstand von 4 führt zu einer Berührung von ~ 0,25% -0,5% der Werte im Baum und dauert ~ 100 ms.

Falscher Name
quelle
BK-Tree im Speicher mit 16 Millionen Zeilen im Speicher? Ich habe mir etwas ähnliches angeschaut, aber mit 1000 Bildern und 2000 Deskriptoren auf jedem Bild war meine Speichergröße riesig.
Stewart
@Stewart - Vieles davon hängt von der Größe Ihres Hash ab. In meinem Fall ist die Hashwertausgabe ein einzelnes 64-Bit-Bitfeld, das ich als int64 speichere. Sie scheinen einen viel größeren Phash-Datentyp zu haben. Ich bin mir auch nicht sicher, wie Suchvorgänge für einen anderen Datentyp wie diesen funktionieren würden. Sind sie noch ein metrischer Raum? Wie berechnet man die Entfernung?
Fake Name
Ich verwende 32-Bit-Deskriptoren mit dem mit opencv gelieferten FLANN-Marcher. Um die Distanz zu berechnen, benutze ich Hamming mit einem Schwellenwert, der auf Lowes Verhältnis basiert. An diesem Punkt bin ich mir nicht sicher, ob es das Beste ist, bei FLANN zu bleiben, das eine KD-Baumstruktur bereitstellt, oder zu einer Lösung zu wechseln, die Ihrer ähnlicher ist. Warum hast du dein eigenes gerollt und dich nicht für so etwas wie libflann entschieden?
Stewart
@Stewart - Ich habe mein eigenes nicht gewürfelt. Ich verwende super langweiliges DFT-basiertes Hashing .
Fake Name
7

MOAR ANTWORTEN!

Ok, ich habe mir endlich die Zeit genommen, eine benutzerdefinierte PostgreSQL-Indizierungserweiterung zu schreiben. Ich habe die SP-GiST-Schnittstelle verwendet .

Das war ziemlich herausfordernd, vor allem, weil Posgres groß ist .

Sowieso ist es hier wie immer auf Github .

In Bezug auf die Leistung ist es derzeit ca. 2-3 Mal langsamer als die Implementierung von Pure-in-Memory in meiner anderen Antwort auf diese Frage, aber es ist so viel praktischer zu verwenden, dass ich diesen Leistungstreffer gerne esse (realistischerweise ist es ca. 50) ms / query - 150 ms / query, was immer noch ziemlich klein ist).

Falscher Name
quelle
Du bist toll! Können Sie eine README-Datei zur Installation hinzufügen? Ich habe nie wirklich etwas in Postgres installiert: P
HypeWolf
1
@HypeWolf - Die Wurzel des Repos enthält eine README- Datei . Deckt das nicht ab, was Sie wollen?
Fake Name
Mein Fehler, ich habe es nicht gesehen, ich bin nicht sicher, wo ich gesucht habe: /
HypeWolf
Habe auch nach der README gesucht. Es befindet sich im Stammordner. Der Link führt zu einem Unterordner. Das war verwirrend.
Luckydonald