FaissをソースコードからビルドしてGPUで利用する
概要
Faissは、Facebook Research (現Meta Research) が開発した近似最近傍探索 (Approximate Nearest Neighbor: ANN) のライブラリです。C++で実装されており、GPUでの実行にも対応しているほか、PythonのWrapperも提供されいます。Pythonパッケージではfaiss-cpu
とfaiss-gpu
の2種類が存在し、公式ではAnacondaでのインストールを推奨しているのですが、PyPIからもパッケージがインストール可能です。
GPU環境でのFaiss実行を試みたのですが、現在PyPIで提供されているfaiss-gpu==1.7.2
では私の環境では正常に動作しませんでした。そのためソースコードからビルドしてインストールしたのでその経緯と、最後にGPU/CPUの簡単なベンチマークを紹介したいと思います。
環境
- Ubuntu: 22.04
- CUDA: 11.8
pypiパッケージで動作しなかったときのエラーメッセージ
Faiss assertion 'err == CUBLAS_STATUS_SUCCESS' failed in void faiss::gpu::runMatrixMult(faiss::gpu::Tensor<float, 2, true>&, bool, faiss::gpu::Tensor<T, 2, true>&, bool, faiss::gpu::Tensor<IndexType, 2, true>&, bool, float, float, cublasHandle_t, cudaStream_t) [with AT = float; BT = float; cublasHandle_t = cublasContext*; cudaStream_t = CUstream_st*] at /__w/faiss-wheels/faiss-wheels/faiss/faiss/gpu/utils/MatrixMult-inl.cuh:265; details: cublas failed (13): (512, 64) x (100000, 64)' = (512, 100000) gemm params m 100000 n 512 k 64 trA T trB N lda 64 ldb 64 ldc 100000
ソースコードからのビルド
基本的にはFaissのINSTALL.md
に書かれている通りにビルドすれば問題ありません。
faiss/INSTALL.md at main · facebookresearch/faiss
インストールに必要なライブラリやパッケージ等がありますので、あらかじめ準備しておく必要があります。私の場合CUDAやPython等はすでに導入済みでしたので、今回はMKLとSWIGをインストールしていきます。
事前準備 - MKL
まずは"BLAS implementation"としてMKLをインストールします。MKLとは、Intelの数値演算ライブラリです。Anacondaでは標準でMKLを利用しているので、FaissがAnaconda環境を推奨しているのでしょう。以下のドキュメントに沿ってaptからインストールできます。
Installing Intel® Performance Libraries and Intel® Distribution for...
$ curl -sfL https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS-2019.PUB | sudo apt-key add -
$ sudo curl -sfL https://apt.repos.intel.com/setup/intelproducts.list -o /etc/apt/sources.list.d/intelproducts.list
$ sudo apt update
$ sudo apt install -y intel-mkl-64bit-2020.4-912
今回はintel-mkl-64bit-2020.4-912
をインストールしました。sudo apt install intel-mkl-64bit
を実行するとパッケージのバージョン一覧が表示され指定を促されるので、その中から最新のものを選択しました。
あとは、インストールした先に各種PATHを通します。利用しているshellの設定ファイル (.bashrc
や.zshrc
)に下記内容を追記します。なお、私の環境ではNumpy実行時にエラーがでたので4行目を追加しています。
# for MKL
export MKL_ROOT_DIR=/opt/intel/mkl
export LD_LIBRARY_PATH=$MKL_ROOT_DIR/lib/intel64:/opt/intel/lib/intel64_lin:$LD_LIBRARY_PATH
export LIBRARY_PATH=$MKL_ROOT_DIR/lib/intel64:$LIBRARY_PATH
export LD_PRELOAD=/opt/intel/mkl/lib/intel64/libmkl_def.so:/opt/intel/mkl/lib/intel64/libmkl_avx2.so:/opt/intel/mkl/lib/intel64/libmkl_core.so:/opt/intel/mkl/lib/intel64/libmkl_intel_lp64.so:/opt/intel/mkl/lib/intel64/libmkl_intel_thread.so:/opt/intel/lib/intel64_lin/libiomp5.so
事前準備 - SWIG
次にPython WrapperのためのSWIGをインストールします。
$ sudo apt -y install swig
ちなみに、過去のソースからのビルド例ではaptのSWIGでは駄目とありましたが(link)、今回の私の環境では問題ありませんでした。
FaissのビルドとPython Wrapperのインストール
それではFaiss本体をビルドしていきます。
$ git clone https://github.com/facebookresearch/faiss
$ cd faiss
$ cmake -B build .
$ make -C build -j faiss
最後にPython Wrapperのビルドとインストールを行います。
$ make -C build -j swigfaiss
$ cd build/faiss/python
$ python setup.py install
インストールが完了したかを確認して、ようやく終了です。
$ pip freeze | grep faiss
faiss==1.7.2
GPUでの実行
それでは試しにGPUで動作するか試してみましょう。Getting Startedのサンプルを利用し、GPUで実行してみます。なお、小さいサンプルサイズだとすぐに終わってしまうので、nb
のサイズを増やしています。
import faiss
import numpy as np
d = 64 # dimension
nb = 1000000 # database size
nq = 10000 # nb of queries
np.random.seed(1234) # make reproducible
xb = np.random.random((nb, d)).astype('float32')
xb[:, 0] += np.arange(nb) / 1000.
xq = np.random.random((nq, d)).astype('float32')
xq[:, 0] += np.arange(nq) / 1000.
gpu_resource = faiss.StandardGpuResources()
cpu_index = faiss.IndexFlatL2(d)
gpu_index = faiss.index_cpu_to_gpu(gpu_resource, 0, cpu_index)
gpu_index.add(xb)
k = 4 # we want to see 4 nearest neighbors
D, I = gpu_index.search(xb, k) # actual search
print(I)
あとはnvidia-smi
やnvitop
等でGPUのUtilizationを見て、実行時にGPUが使われていればOKです。このサンプルスクリプトは約25秒で終了しました。
ベンチマーク
せっかくFaissが動作するようになったので、GPUとGPUで実行環境による性能差を比較してみましょう。
今回はNLPの事例として、単語埋め込み(Word Embedding)のベクトルを対象に全単語の類似単語を求める操作の実行時間を計測します。それぞれ単語ベクトルは300次元で密なベクトルです。実験条件は以下の通りです。
- 単語ベクトル:chiVe Version
v1.2 mc5
- 語彙数 3,197,456
- 次元数: 300
- 求める近傍の点: 10個
- Faissのインデックス:
IndexFlatL2
つまり、約319万のベクトルのANNのインデックスに対して、約319万回の近似最近傍探索の操作を行って各10件の近傍点を求めるという操作を行います。
コード
単語ベクトルの読み込み部分は割愛し、差分のあるインデックス作成部分のみ示します。
# Run with CPU
index = faiss.IndexFlatL2(d)
index.add(vectors)
# Run with GPU
gpu_resource = faiss.StandardGpuResources()
cpu_index = faiss.IndexFlatL2(d)
gpu_index = faiss.index_cpu_to_gpu(gpu_resource, 0, cpu_index)
gpu_index.add(vectors)
実験に使用したソースコードは、下記レポジトリにあります。
結果
実行結果は以下のようになりました。なお実行時の計算機リソース使用状況は、CPUの場合は全24スレッド使用しLoad Averageで11~12程度、使用率はトータルで50%程度でした。GPUの場合は、GPUメモリ使用率27%、GPUのUTL使用率は100%でした。
Method | Device | Time (hh:mm:ss) |
---|---|---|
GPU | GTX 3090 (Mem:24GB) | 00:07:26 |
CPU | Ryzen 9 5900 (12core/24thread) | 18:36:19 |
結果としては、GPUは7分で終了したのに対し、CPUは18時間もかかりました。圧倒的ですね……! CPUはマルチスレッドで動かしてもこれだけの時間を要したことを考えると、GPUの超並列処理の強さを改めて実感しました。
(補足) Poetryのインストール
最後にPoetryでインストールする方法も書いておきます。なお、ビルドしたfaissのディレクトリはここでは/home/yag_ays/local/src/faiss/
にあります。
$ poetry add /home/yag_ays/local/src/faiss/build/faiss/python
Updating dependencies
Resolving dependencies... (0.5s)
Writing lock file
Package operations: 1 install, 0 updates, 0 removals
• Installing faiss (1.7.2 /home/yag_ays/local/src/faiss/build/faiss/python)
pyproject.toml
には以下のように記述されます。
[tool.poetry.dependencies]
faiss = {path = "/home/yag_ays/local/src/faiss/build/faiss/python"}
あとは実際に動作するかを確認して完了です。
In [1]: import faiss
In [2]: faiss.get_num_gpus()
Out[2]: 1
参考
- facebookresearch/faiss: A library for efficient similarity search and clustering of dense vectors.
- Running on GPUs · facebookresearch/faiss Wiki
- GPU対応の類似検索(最近傍探索)ライブラリ Faissの紹介 part1 導入/チュートリアル – Rest Term
- Intel MKL, IPP, TBB, DALL, MPI(Performance ライブラリ)のインストール(Ubuntu 上)
- intelMKLを用いたライブラリとNumpyを併用した際に生じるエラーの解消 - Qiita
Discussion
素晴らしい記事をありがとうございます😃
cuda 12.0
faiss-gpu==1.7.2
の組み合わせだと、pipでインストールできました。
ご参考まで…。
報告ありがとうございます。記事上部にも記載させていただきます。