Bonjour, c'est le jour 23 du Calendrier de l'Avent ABEJA.
Connaissez-vous une bibliothèque appelée faiss? La bibliothèque de recherche de quartier de Facebook Resarch est très facile à utiliser et contient de nombreux tutoriels, je l'utilise donc fréquemment pour les affaires et les loisirs. Je vais. Cependant, une bibliothèque aussi pratique a aussi ses faiblesses. Quand j'essaie de faire quelque chose d'un petit créneau, je dis simplement: "Eh bien, expliquez les détails et ayez une idée du code de test (-д ☆)." J'ai appris à l'utiliser en lisant réellement le code de test et parfois en lisant le code C ++ du corps principal, donc je l'écrirai avec un rappel pour moi-même à l'avenir.
Dans de rares cas, vous souhaitez obtenir les fonctionnalités elles-mêmes, plutôt que de rechercher les fonctionnalités détenues par faiss. Par exemple, avec faiss.IndexFlatL2
, le montant de la caractéristique lui-même peut être obtenu à partir de l'attribut xb
. Vous pouvez également le convertir en numpy.ndarray
en utilisant la fonction faiss.vector_float_to_array
.
>>> 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)
Vous pouvez vérifier à partir de quel attribut le montant de la fonctionnalité lui-même peut être obtenu à partir de l'en-tête tel que faiss / IndexFlat.h.
numpy.ndarray
=> float * x
[faiss / c_api / pour utiliser des fonctions d'interface swig non implémentées dans faiss / python / faiss.py Si vous regardez IndexFlat_c.h](https://github.com/facebookresearch/faiss/blob/master/c_api/IndexFlat_c.h), vous pouvez rencontrer float * x
. Si vous utilisez faiss.swig_ptr
pour numpy.ndarray
, vous pouvez le convertir en un pointeur de swig, vous pouvez donc utiliser une fonction légèrement de niche.
>>> x = np.random.random(10).astype(np.float32)
>>> faiss.swig_ptr(x)
<Swig Object of type 'faiss::IndexReplicasTemplate< faiss::Index >::component_t *' at 0x7fe8a57c3b10>
Jusqu'à présent, de nombreuses fonctions de base ont été introduites, mais à partir de maintenant, elles seront un peu plus avancées. A propos de la gestion du sous-espace dans faiss.
Le premier est le calcul de la distance dans le sous-espace. En utilisant compute_distance_subset
, cela peut être réalisé comme suit.
>>> 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)
Il est à noter que compute_distance_subset
ne calcule que la distance du sous-ensemble, et ne calcule pas le voisinage de K.
Ensuite, comment créer le sous-espace lui-même. Ceci peut être réalisé en utilisant copy_subset_to
. Si vous regardez Fractionner et fusionner les index, vous pouvez le comprendre à peu près, donc je n'écrirai que les notes.
Le premier point est décrit dans le document, mais il ne peut être utilisé qu'avec ʻIndex IVF`.
Le deuxième point est qu'il y a des difficultés à créer un sous-espace. Comme vous pouvez le voir dans le Code source, seules les copies continues et périodiques sont prises en charge.
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;
Par défaut, des ID consécutifs sont attribués, mais vous pouvez également utiliser votre propre système d'identification en utilisant ʻIndexIDMap`. Si vous regardez Faiss ID mapping, vous pouvez le voir, mais l'ID unique est le suivant. Vous pouvez utiliser le système.
>>> 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]]))
Il y a une mise en garde ici. Puisque faiss.IndexIDMap
ne mappe que le système d'index et d'ID d'origine, si vous utilisez une ancienne version de faiss, vous obtiendrez une erreur de segmentation dans les situations suivantes où l'index peut être GC. La dernière version de faiss (1.5.3) semble résoudre ce problème.
>>> index = faiss.IndexIDMap(faiss.IndexFlatL2(d))
>>> index.add_with_ids(xb, ids)
Segmentation fault (core dumped)
Soit dit en passant, il n'est pas très utile, mais vous pouvez également rechercher l'index d'origine.
>>> 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]]))
J'ai introduit une petite fonction de niche de faiss, qui est une bibliothèque de recherche de quartier de Facebook Resarch, avec un rappel pour moi-même. Il semble que ce sera un article de niche à mi-chemin et qui le lira ... mais j'espère pouvoir jouer le rôle d'une seule personne. Je continuerai à l'ajouter si j'essaye une petite fonction de niche de faiss.
Recommended Posts