Hallo, dies ist Tag 23 des ABEJA-Adventskalenders.
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 (-д ☆)." 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.
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.
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>
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;
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]]))
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