パッケージhnsw 、階層的なナビゲーション可能な小さな世界グラフを実装します。ここでどのように機能するかを読むことができます。本質的に、それらは高次元ベクトルデータを使用した高速の近似近隣検索を可能にします。
このパッケージは、お気に入りのベクトルデータベース(Pinecone、Weaviateなど)に代わるメモリ内の代替品と考えることができます。重要な操作だけを実装します。
| 手術 | 複雑 | 説明 |
|---|---|---|
| 入れる | ベクトルをグラフに挿入します | |
| 消去 | グラフからベクトルを削除します | |
| 検索 | ベクトルの最近隣人を検索します | |
| 見上げる | IDでベクトルを取得します |
注記
複雑さは概算です
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] すべてのグラフ操作はメモリ内ですが、 hnsw永続的なストレージからロード/保存するための機能を提供します。
io.Reader / io.Writerインターフェイスの場合、 Graph.ExportとGraph.Importを使用します。
バックエンドとして単一のファイルを使用している場合、HNSWは代わりに便利なSavedGraphタイプを提供します。
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 )
}詳細をご覧ください:
グラフに高速バイナリエンコードを使用するため、ディスク速度でほぼ保存/ロードすることが期待できます。私のM3 MacBookでは、これらのベンチマークの結果が得られます。
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
256の寸法で100個のベクトルのグラフを保存/ロードするとき。
概して、グラフのパフォーマンスに及ぼすことができる最大の効果は、データの次元を減らすことです。 1536の寸法(OpenAIデフォルト)では、デフォルトパラメーターの下でのクエリプロセスの70%が距離関数に使用されます。
遅延 /待ち時間に苦労している場合は、次のことを検討してください。
そして、あなたが過剰な記憶の使用に苦労しているなら、次のことを考慮してください。
Graph.M (各ノードが持つことができる隣人の最大数)Graph.Ml (レベル生成パラメーター) グラフのメモリオーバーヘッドは次のように見えます。
どこ:
あなたはそれを推測することができます:
256寸法のグラフの例では、
そして、メモリの成長はほとんど線形です。