Package hnsw implémente les graphiques hiérarchiques de petit monde navigable dans Go. Vous pouvez lire comment ils fonctionnent ici. Essentiellement, ils permettent des recherches de voisins les plus proches plus rapides avec des données vectorielles à haute dimension.
Ce package peut être considéré comme une alternative en mémoire à votre base de données vectorielle préférée (par exemple Pinecone, Weavate). Il implémente uniquement les opérations essentielles:
| Opération | Complexité | Description |
|---|---|---|
| Insérer | Insérer un vecteur dans le graphique | |
| Supprimer | Supprimer un vecteur du graphique | |
| Recherche | Recherchez les voisins les plus proches d'un vecteur | |
| Chercher | Récupérer un vecteur par id |
Note
Les complexités sont approximatives où
go get github.com/coder/hnsw@main
g := hnsw . NewGraph [ int ]()
g . Add (
hnsw . MakeNode ( 1 , [] float32 { 1 , 1 , 1 }),
hnsw . MakeNode ( 2 , [] float32 { 1 , - 1 , 0.999 }),
hnsw . MakeNode ( 3 , [] float32 { 1 , 0 , - 0.5 }),
)
neighbors := g . Search (
[] float32 { 0.5 , 0.5 , 0.5 },
1 ,
)
fmt . Printf ( "best friend: %v n " , neighbors [ 0 ]. Vec )
// Output: best friend: [1 1 1] Bien que toutes les opérations de graphiques soient en mémoire, hnsw fournit des installations pour charger / économiser du stockage persistant.
Pour une interface io.Reader / io.Writer , utilisez Graph.Export et Graph.Import .
Si vous utilisez un seul fichier comme backend, HNSW fournit à la place un type SavedGraph pratique:
path := "some.graph"
g1 , err := LoadSavedGraph [ int ]( path )
if err != nil {
panic ( err )
}
// Insert some vectors
for i := 0 ; i < 128 ; i ++ {
g1 . Add ( hnsw . MakeNode ( i , [] float32 { float32 ( i )}))
}
// Save to disk
err = g1 . Save ()
if err != nil {
panic ( err )
}
// Later...
// g2 is a copy of g1
g2 , err := LoadSavedGraph [ int ]( path )
if err != nil {
panic ( err )
}Voir plus:
Nous utilisons un encodage binaire rapide pour le graphique, vous pouvez donc vous attendre à enregistrer / charger presque à une vitesse de disque. Sur mon M3 MacBook, j'obtiens ces résultats de référence:
goos: darwin
goarch: arm64
pkg: github.com/coder/hnsw
BenchmarkGraph_Import-16 4029 259927 ns/op 796.85 MB/s 496022 B/op 3212 allocs/op
BenchmarkGraph_Export-16 7042 168028 ns/op 1232.49 MB/s 239886 B/op 2388 allocs/op
PASS
ok github.com/coder/hnsw 2.624s
Lors de l'enregistrement / chargement d'un graphique de 100 vecteurs avec 256 dimensions.
Dans l'ensemble, le plus grand effet que vous puissiez avoir sur les performances du graphique consiste à réduire la dimensionnalité de vos données. À 1536 Dimensions (Openai par défaut), 70% du processus de requête sous paramètres par défaut est dépensé dans la fonction de distance.
Si vous avez du mal avec la lenteur / latence, considérez:
Et, si vous avez du mal avec une utilisation excessive de la mémoire, considérez:
Graph.M (le nombre maximum de voisins chaque nœud peut avoir)Graph.Ml (le paramètre de génération de niveau) La mémoire de mémoire d'un graphique ressemble:
où:
Vous pouvez déduire cela:
Dans l'exemple d'un graphique avec 256 dimensions, et
Et la croissance de la mémoire est principalement linéaire.