はじめに
Hnswlibは、近似最近棒探索のベンチマークでも上位に位置する、Hierarchical Navigable Small World graphsによるC++の近似最近傍探索ライブラリである。Node.jsのbindingがなかったので、腱鞘炎のリハビリとnpm作成の勉強がてら作成していた。ずっと前に。
インストール
普通にnpmをインストールすれば使える(Windowsではnativeなnpmはビルドツールを別で入れる必要があるみたいですね)。ブラウザでは動かない。サーバーサイドで利用することを想定してる。
$ npm i hnswlib-node
使い方
アイテムベクトルをnumberのArrayで表現し、それをインデックスに追加する。
import { HierarchicalNSW } from 'hnswlib-node'; const numDimensions = 8; // ベクトルの次元数 const maxElements = 10; // 検索インデックスに入れるベクトルの最大数 // メトリックと次元数を指定してインスタンスを作成し, ベクトルの最大数を与えて初期化する. const index = new HierarchicalNSW('l2', numDimensions); index.initIndex(maxElements); // numberのArrayで表されるベクトルを, 添字とともに検索インデックスに追加する. for (let i = 0; i < maxElements; i++) { const point = new Array(numDimensions); for (let j = 0; j < numDimensions; j++) point[j] = Math.random(); index.addPoint(point, i); } // 保存する. index.writeIndexSync('foo.dat');
検索は、numberのArrayによるクエリベクトルと近傍数を与えればよい。
import { HierarchicalNSW } from 'hnswlib-node'; // メトリックと次元数を指定してインスタンスを作成し, 検索インデックスを読み込む. const numDimensions = 8; const index = new HierarchicalNSW('l2', numDimensions); index.readIndexSync('foo.dat'); // 適当なクエリベクトルを用意する. const query = new Array(numDimensions); for (let j = 0; j < numDimensions; j++) query[j] = Math.random(); // 近傍数とともクエリベクトルを与えて検索する. const numNeighbors = 3; const result = index.searchKnn(query, numNeighbors); // 距離とラベルによる検索結果が得られる. console.table(result);
hnswlibのver. 0.7.0で追加されたmark_deleteや検索結果のfilteringにも対応している。
おわりに
勉強がてら作っていたnpmだが、ChatGPTが流行って、LangChainのJS/TS版のベクトル検索に採用されて利用者が増えた (実際にベクトル検索使ってる人は限られるだろうが) 。dalaiなんかもそうだが、Node.jsコミュニティは大規模言語モデル (Large Language Models, LLMs) への反応が速かった。今後、LLMsを容易に利用できるかは、重要になりそう。ちなみに、hnswlibのRuby bindingも開発を続けてます。