[PYTHON] Eine kleine Nischenfunktion Einführung von Faiss

Hallo, dies ist Tag 23 des ABEJA-Adventskalenders.

Einführung

Kennen Sie eine Bibliothek namens faiss? Die Nachbarschaftssuchbibliothek von Facebook Resarch ist sehr einfach zu verwenden und enthält viele Tutorials, sodass ich sie häufig sowohl für Unternehmen als auch für Hobbys verwende. Ich werde. Eine solche bequeme Bibliothek hat jedoch auch ihre Schwächen. Wenn ich versuche, eine kleine Nische zu schaffen, sage ich einfach: "Nun, erkläre die Details und bekomme ein Gefühl für den Testcode (-д ☆)." Screenshot_2019-12-23 facebookresearch faiss.png Ich habe gelernt, wie man es benutzt, indem ich den Testcode und manchmal den C ++ - Code des Hauptteils gelesen habe, also werde ich ihn in Zukunft mit einer Erinnerung für mich selbst schreiben.

Eine kleine Einführung in die Nischenfunktion

Erwerb von Fiss-Funktionen

In seltenen Fällen möchten Sie die Funktionen selbst abrufen, anstatt nach den Funktionen zu suchen, die faiss enthält. Beispielsweise kann mit faiss.IndexFlatL2 der Merkmalsbetrag selbst aus dem Attribut xb erhalten werden. Sie können es auch mit der Funktion "faiss.vector_float_to_array" in "numpy.ndarray" konvertieren.

>>> d = 2
>>> nb = 10
>>> xb = np.random.random((nb, d)).astype(np.float32)
>>>
>>> index = faiss.IndexFlatL2(d)
>>> index.add(xb)
>>>
>>> index.xb
<faiss.swigfaiss.FloatVector; proxy of <Swig Object of type 'std::vector< float > *' at 0x7f7ff26dede0> >
>>> faiss.vector_float_to_array(index.xb)
array([0.70434606, 0.8172881 , 0.27514696, 0.04918063, 0.16584012,
       0.58303493, 0.83627784, 0.7318148 , 0.91633004, 0.16084996,
       0.6760264 , 0.65586466, 0.45432937, 0.35858378, 0.0895841 ,
       0.3424391 , 0.6606455 , 0.7392444 , 0.07704416, 0.13714503],
      dtype=float32)

Sie können überprüfen, von welchem Attribut der Feature-Betrag selbst aus dem Header wie faiss / IndexFlat.h abgerufen werden kann.

Konvertierung von numpy.ndarray => float * x

faiss / c_api / um Funktionen der swig-Schnittstelle zu verwenden, die nicht in [faiss / python / faiss.py] implementiert sind (https://github.com/facebookresearch/faiss/blob/master/python/faiss.py) Wenn Sie sich IndexFlat_c.h ansehen, können Sie auf "float * x" stoßen. Wenn Sie "faiss.swig_ptr" für "numpy.ndarray" verwenden, können Sie es in einen Zeiger von swig konvertieren, sodass Sie eine solche Nischenfunktion verwenden können.

>>> x = np.random.random(10).astype(np.float32)
>>> faiss.swig_ptr(x)
<Swig Object of type 'faiss::IndexReplicasTemplate< faiss::Index >::component_t *' at 0x7fe8a57c3b10>

Unterraum behandeln

Bis jetzt wurden viele Grundfunktionen eingeführt, aber von nun an wird es etwas weiter fortgeschritten sein. Über den Umgang mit Subräumen in Fissuren. Erstens ist die Entfernungsberechnung im Unterraum. Mit "compute_distance_subset" kann dies wie folgt realisiert werden.

>>> def compute_distance_subset(index, xq, subset):
...     n, _ = xq.shape
...     _, k = subset.shape
...     distances = np.empty((n, k), dtype=np.float32)
...     index.compute_distance_subset(
...             n, faiss.swig_ptr(xq),
...             k, faiss.swig_ptr(distances),
...             faiss.swig_ptr(subset)
...     )
...     return distances
... 
>>>
>>> d = 2
>>> nb = 10000
>>> nq = 10
>>> k = 5
>>> xb = np.random.random((nb, d)).astype(np.float32)
>>> xq = np.random.random((nq, d)).astype(np.float32)
>>> subset = np.random.choice(range(nb), (nq, k))
>>> 
>>> index = faiss.IndexFlatL2(d)
>>> index.add(xb)
>>> 
>>> compute_distance_subset(index, xq, subset)
array([[0.04731181, 0.1585833 , 0.4276843 , 0.02083743, 0.14153683],
       [0.55289364, 0.19499591, 0.24127454, 0.16293366, 0.02044217],
       [0.61750704, 0.48981428, 0.51042193, 0.12334089, 0.55514073],
       [0.5959296 , 0.6604827 , 0.20945217, 0.10136123, 0.01619768],
       [0.13882631, 0.16818088, 0.01572821, 0.17454663, 0.03992677],
       [0.46265444, 0.70609426, 0.49902472, 0.730565  , 0.09248901],
       [1.1638596 , 1.1041545 , 0.73789394, 0.60920525, 0.21328084],
       [0.02405633, 0.00557276, 0.6880306 , 0.821055  , 0.0421453 ],
       [0.08726364, 0.33441633, 0.15067156, 0.28792596, 0.30785137],
       [0.04219329, 0.747885  , 0.01912764, 0.19305223, 0.51132184]],
      dtype=float32)

Beachten Sie, dass compute_distance_subset nur den Abstand der Teilmenge berechnet und nicht die Umgebung von K.

Als nächstes wird beschrieben, wie der Unterraum selbst erstellt wird. Dies kann durch Verwendung von copy_subset_to erreicht werden. Wenn Sie sich [Aufteilen und Zusammenführen von Indizes] ansehen (https://github.com/facebookresearch/faiss/wiki/Special-operations-on-indexes#splitting-and-merging-indexes), können Sie es grob verstehen, sodass ich nur die Notizen schreibe. Der erste Punkt ist im Dokument geschrieben, kann aber nur mit "IndexIVF" verwendet werden. Der zweite Punkt ist, dass es einige Schwierigkeiten gibt, einen Unterraum zu erstellen. Wie Sie dem Quellcode entnehmen können, werden nur fortlaufende und regelmäßige Kopien unterstützt.

faiss/IndexIVF.h


/** copy a subset of the entries index to the other index
 *
 * if subset_type == 0: copies ids in [a1, a2)
 * if subset_type == 1: copies ids if id % a1 == a2
 * if subset_type == 2: copies inverted lists such that a1
 *                      elements are left before and a2 elements are after
 */
virtual void copy_subset_to (IndexIVF & other, int subset_type,
                             idx_t a1, idx_t a2) const;

Verwenden Sie Ihr eigenes Identitätssystem

Standardmäßig werden aufeinanderfolgende IDs zugewiesen, aber Sie können Ihr eigenes ID-System verwenden, indem Sie "IndexIDMap" verwenden. Wenn Sie sich Faiss ID Mapping ansehen, können Sie es sehen, aber die eindeutige ID lautet wie folgt. Sie können das System verwenden.

>>> d = 2
>>> nb = 10000
>>> nq = 10
>>> k = 2
>>> xb = np.random.random((nb, d)).astype(np.float32)
>>> xq = np.random.random((nq, d)).astype(np.float32)
>>> ids = np.arange(nb) * 10
>>>
>>> index = faiss.IndexFlatL2(d)
>>> custom_index = faiss.IndexIDMap(index)
>>> custom_index.add_with_ids(xb, ids)
>>> custom_index.search(xq, k)
(array([[1.97887421e-05, 2.86698341e-05],
       [1.38282776e-05, 6.81877136e-05],
       [4.52995300e-06, 1.12056732e-05],
       [8.55922699e-05, 9.26256180e-05],
       [1.41859055e-05, 1.38044357e-04],
       [2.40206718e-05, 4.58657742e-05],
       [6.55651093e-06, 3.46302986e-05],
       [5.24520874e-06, 1.35898590e-05],
       [2.90870667e-05, 3.90410423e-05],
       [2.38418579e-07, 2.86102295e-05]], dtype=float32), array([[38210, 66060],
       [51500, 97890],
       [17100, 97780],
       [42300, 51430],
       [ 3790, 63660],
       [19050, 26470],
       [22070, 45900],
       [39140,  4190],
       [10040,  7850],
       [14390, 48690]]))

Hier gibt es eine Einschränkung. Da faiss.IndexIDMap nur das ursprüngliche Index- und ID-System abbildet, wird bei Verwendung einer älteren Version von faiss in den folgenden Situationen, in denen der Index GC-fähig sein kann, ein Segmentierungsfehler angezeigt. Die neueste Version von faiss (1.5.3) scheint dieses Problem zu lösen.

>>> index = faiss.IndexIDMap(faiss.IndexFlatL2(d))
>>> index.add_with_ids(xb, ids)
Segmentation fault (core dumped)

Übrigens gibt es nicht viel Verwendung, aber Sie können auch nach dem ursprünglichen Index suchen.

>>> index.search(xq, k)
(array([[1.97887421e-05, 2.86698341e-05],
       [1.38282776e-05, 6.81877136e-05],
       [4.52995300e-06, 1.12056732e-05],
       [8.55922699e-05, 9.26256180e-05],
       [1.41859055e-05, 1.38044357e-04],
       [2.40206718e-05, 4.58657742e-05],
       [6.55651093e-06, 3.46302986e-05],
       [5.24520874e-06, 1.35898590e-05],
       [2.90870667e-05, 3.90410423e-05],
       [2.38418579e-07, 2.86102295e-05]], dtype=float32), array([[3821, 6606],
       [5150, 9789],
       [1710, 9778],
       [4230, 5143],
       [ 379, 6366],
       [1905, 2647],
       [2207, 4590],
       [3914,  419],
       [1004,  785],
       [1439, 4869]]))

Zusammenfassung

Ich habe faiss eingeführt, eine Nachbarschaftssuchbibliothek von Facebook Resarch, mit einer kleinen Nischenfunktion, die mich daran erinnert. Es scheint, dass es auf halbem Weg ein Nischenartikel sein wird und wer ihn lesen wird ... aber ich hoffe, ich kann die Rolle einer Person spielen. Ich werde es weiter hinzufügen, wenn ich eine kleine Nischenfunktion von faiss versuche.

Recommended Posts

Eine kleine Nischenfunktion Einführung von Faiss
Eine kleine Überprüfung von Pandas 1.0 und Dask
Einführung der neuen Sprachfeaturextraktionsbibliothek Surfboard
Einführung von PyGMT
Einführung von Python
Fügen Sie nach und nach eine Liste der Funktionen der Numpy-Bibliothek hinzu --a
[PyTorch] Ein wenig Verständnis von CrossEntropyLoss mit mathematischen Formeln
[Python] [Gracenote Web API] Eine kleine Anpassung von Pygn
Einführung von trac (Windows + trac 1.0.10)
Einführung von ferenOS 1 (Installation)
Einführung des Virtualenv-Wrappers
Fügen Sie nach und nach eine Liste der Funktionen der Numpy-Bibliothek hinzu --c
[Einführung in AWS] Memorandum zum Erstellen eines Webservers auf AWS
Ich habe ein wenig versucht, das Verhalten der Zip-Funktion