Faiss(2):编程环境搭建

1. 环境

硬件平台

  • cpu: Intel Skylake系列, 20core
  • memory: 32GB
  • GPU: NVIDIA Tesla P4

软件环境

  • OS :Ubuntu16.04
  • NVIDIA Driver : 440.33.01
  • CUDA : 10.2
  • CONDA : 4.8.0
  • Faiss源代码版本: 1.6.1

2. 通过Conda安装

这里基于GPU安装Faiss

安装步骤

1. 安装系统更新

sudo apt-get update
sudo apt-get upgrade -y

2. 下载cuda

安装驱动和cuda有两种方式:

  1. http://www.nvidia.com/object/unix.html 通过nvidia官网下载驱动包,选择合适版本驱动,再单独安装cuda.
  2. https://developer.nvidia.com/cuda-downloads 直接安装带驱动的cuda。
    我这里选择第一种方式,先安装driver,再安装cuda。下载好NVIDIA-Linux-x86_64-440.33.01.run和cuda_10.2.89_440.33.01_linux.run
sudo chmod +x NVIDIA-Linux-x86_64-440.33.01.run
sudo chmod +x cuda_10.2.89_440.33.01_linux.run

3. 禁用nouveau

# 新建blacklist-nouveau.conf文件
sudo vim /etc/modprobe.d/blacklist-nouveau.conf

# 写入如下内容
blacklist nouveau
blacklist lbm-nouveau
options nouveau modeset=0
alias nouveau off
alias lbm-nouveau off

# 重新生成 kernel initramfs
sudo update-initramfs -u

# 重启系统
reboot

重启系统后验证nouveau是否被禁用, 若无输出则成功。

lsmod | grep nouveau

4. 安装驱动及cuda

安装cuda需在root用户和字符界面下进行,即需要关闭图形界面。

# 关闭图形界面
sudo service lightdm stop

# 安装driver
sh NVIDIA-Linux-x86_64-440.33.01.run --no-opengl-files

# 安装cuda, 此时取消driver的勾选
sh cuda_10.2.89_440.33.01_linux.run

# 重启图形界面
sudo service lightdm start

5. 验证是否安装成功

nvidia-smi

若能显示显卡的基本信息,则表示安装成功。

6. 安装Anaconda

通过官网https://www.anaconda.com/distribution/选择对应的版本进行安装。

chmod +x Anaconda2-2019.10-Linux-x86_64.sh
./Anaconda2-2019.10-Linux-x86_64.sh

#验证是否安装成功
conda -V

7. 安装faiss

这里推荐通过Anaconda2来进行安装,因为Facebook会将稳定版的Faiss发布到pytorch中,而且它会自动安装所有的依赖。

# 安装gpu版本
conda install faiss-gpu cudatoolkit=10.2 -c pytorch # For CUDA10

#安装cpu版本
conda install faiss-cpu -c pytorch

由于我安装的是gpu版本,所以cpu版本命令没有验证。

8. 验证是否安装成功

python
import faiss

没有报错,说明安装成功

问题记录

Q1. 运行 demo程序时,显示
“CUDA error 35 CUDA driver version is insufficient for CUDA runtime version”
A1: 这是因为一开始我安装cuda时没有选择nvidia driver,导致部分API不可用。重装driver就可以了。

Q2. 安装完NVIDIA driver后,出现循环登录,无法进入系统界面。
A2: 一开始我使用直接通过cuda安装所有包(包括nvidia driver)的方式安装,导致驱动安装好后一直停留在登录界面无限循环,这是因为cuda安装时默认不会添加"–no-opengl-files"参数。
卸载nvidia driver,单独重新安装后即可。

sudo /usr/bin/nvidia-uninstall
./sh NVIDIA-Linux-x86_64-440.33.01.run --no-opengl-files

3. 通过源代码安装

之前通过Anaconda的方式安装简单方便,对于普通的应用编程足够使用,但是若要深入了解Faiss的软件架构和具体实现或者对推荐系统的执行流程细化分析则仅仅调用API还不够,可能需要修改并重新编译Faiss的源代码。这一部分就按照这种需求重新搭建Faiss的编程环境。

安装步骤

因为有前面的安装经历,且基于同一个平台安装,所以步骤1-5相同。

6. 安装依赖库OpenBLAS
# 下载OpenBLAS源代码
git clone https://github.com/xianyi/OpenBLAS.git

# 安装依赖工具gfortran
apt-get install gfortran

# 编译
cd OpenBLAS
make FC=gfortran

# 安装在/opt目录下
make install 

#将编译好的动态库链接至/usr/lib目录下
ln -s /opt/OpenBLAS/lib/libopenblas.so  /usr/lib/libopenblas.so

# 将OpenBLAS添加至环境变量
vim /etc/profile

LD_LIBRARY_PATH=/opt/OpenBLAS/lib
export LD_LIBRARY_PATH
7. 安装依赖库LAPACK
# 下载源代码
wget https://github.com/Reference-LAPACK/lapack/archive/v3.9.0.tar.gz

tar v3.9.0.tar.gz
cd lapack-3.9.0

根据平台的特点,将INSTALL目录下对应的make.inc.XXX复制一份到 lapack-3.9.0目录下,并命名为make.inc。因为我这里用的是gfortran编译器 因为这里使用gfortran编译器,所以这里选择INSTALL/make.inc.gfortran

cp ./INSTALL/make.inc.gfortran ./
mv make.inc.gfortran make.inc

修改Makefile文件,因为lapack以来于blas库,所以需要做如下修改(注释第一句话,去掉注释第二句话):

#lib: lapacklib tmglib
lib: blaslib variants lapacklig tmglib

编译LAPACK

make

# 进入LAPACK目录,这个目录包含lapack的C语言接口文件
cd LAPACK

# 继续编译
make

由于lapack没有提供"make install"命令,所以需要手动安装

# 将lapacke的头文件复制到系统头文件目录
cp include/*.h /usr/include  

# 将生成的所有库文件复制到系统库目录 
cd ..
cp *.a /usr/lib
8. 安装faiss

下载Faiss源代码
下载地址:https://github.com/facebookresearch/faiss

git clone https://github.com/facebookresearch/faiss

生成makefile文件

# configure会生成makfile文件,因为我基于GPU安装,所以需要指定cuda目录
./configure --with-cuda=/usr/local/cuda-10.2 --with-python=python2

编译安装

#编译
makemake -j32

# 安装
make install

这里采用默认的安装方式,安装完成后在/usr/local/include下可以看到faiss目录,包含各种头文件。
也可以指定安装目录,在./configure时指定。

9. 编译安装SWIG

因为在应用编程时使用python语言,所以需要将faiss的C++接口封装成Python的方法。
SWIG是个帮助使用C或者C++编写的软件能与其它各种高级编程语言进行嵌入联接的开发工具。SWIG能应用于各种不同类型的语言包括常用脚本编译语言例如Perl, PHP, Python, Tcl, Ruby and PHP。

下载SWIG4
我下载时最新版本为4.0.1

wget https://sourceforge.net/projects/swig/files/swig/swig-4.0.1/swig-4.0.1.tar.gz

tar xvf swig-4.0.1.tar.gz

配置swig

# 安装必要组件
apt-get install libpcre3 libpcre3-dev

cd swig-4.0.1
./configure

make
make install

生成python包

cd faiss
make py

# 会在faiss目录下生成python目录
cd python
make install

安装完成后在"/usr/local/lib/python2.7/dist-packages"目录下会生成对应的faiss包。

问题记录

编译Lapack报错

编译Lapack时报错:
“recipe for target ‘znep.out’ failed”

这是因为内核只会将一部分内存分配给编译过程,以防止可能发生错误分配大量内存的情况。但是lapack的编译需要大量内存,导致这里报错。可以使用如下命令解除内存分配的限制:

ulimit -s unlimited

缺乏Python头文件

编译faiss的python接口时报错,
“swigfaiss.cpp:178:21: fatal error: Python.h: No such file or directory”

这是因为swig会用到python的源代码,而默认情况下是没有安装这个版本的,所以只要安装python的开发版就好了。

# For python2
sudo apt-get install python-dev

# For python3
sudo apt-get install python3-dev

如果还不行,可以查看"/usr/include/python2.7"目录下查看是否有Python.h文件,大概率是有的。那么修改faiss下的makefile.inc配置文件。

cd faiss
vim makefile.inc

PYTHONCFLAGS =  -I/usr/include/python2.7 -I/usr/local/include/python2.7 -I

makefile.inc文件里指定的python头文件目录与实际情况不同,所以在这里手动修改该文件。

注: 我使用的是python2.7,如果用其他版本的python,则在对应版本下查找和修改。

无法正常导入faiss包

可能由于我之前安装过conda环境的原因,我在生成faiss的python包,make install后,只能在当前的目录下可以正常"import faiss",在其他目录则会报下列错误:

>>> import faiss
Traceback (most recent call last):
  File "", line 1, in <module>
  File "build/bdist.linux-x86_64/egg/faiss/__init__.py", line 49, in <module>
  File "build/bdist.linux-x86_64/egg/faiss/swigfaiss.py", line 13, in <module>
ImportError: cannot import name _swigfaiss
>>>

首先在faiss/python目录下可以import,说明肯定是有可用的包的,而其他目录无法使用可能是由于路径指向的问题。
经测,import faiss时导入的是faiss/python/faiss下的包,而该包还依赖于上一级目录下的其他文件,所以将整个faiss/python目录下的内容转移出来也是可以的。
解决方法如下:

cd faiss/python

# 将faiss下的内容转移到上一级目录
mv faiss/* .

cd ..
cp -R python/* /usr/local/lib/python2.7/dist-packages/faiss

# 修改easy-install.pth文件中faiss的路径
vim easy-install.pth

    import sys; sys.__plen = len(sys.path)
    ./faiss/faiss-1.6.1-py2.7.egg
    import sys; new = sys.path[sys.__plen:]; del sys.path[sys.__plen:]; p = getattr(sys, '__egginsert', 0); sys.path[p:p] = new; sys.__egginsert = p + len(new)

运行性能差距较大

使用源代码的方式安装完Faiss-GPU环境后,运行与conda-Faiss-GPU环境下相同的测试程序,发现在相同参数下,部分内容性能对比差距十分明显,如下:

Conda-Faiss-GPU Faiss-GPU
train 9.09s 351.11s
add 4.32s 16.77s
trans 1.09s 4.57s
search 0.3s 0.29s

从表格上看,Faiss-GPU在训练、添加索引和转移索引三个阶段比conda版本要慢很多,而search阶段几乎相同。在应用程序中,前三个阶段主要是在CPU中进行,最后的search阶段在GPU中进行。所以猜测这是由源代码安装时只编译了GPU版本,导致CPU运行相率很低。

我通过修改GPU_time.py的代码将Index的train、add等操作转移到GPU中进行后能显著提高两者的速度,分别花费122s和7.73s,说明我之前的推测是正确的,但与原数据仍有不小的差距。

[2020.4.9更新]

最近在研究CPU 搜索过程的时候发现,在根据实验平台核数设置多核运行export OMP_WAIT_POLICY=PASSIVE 和 export OMP_NUM_THREAD=20 后运行测试程序,train和add时间分别下降到18.85s和5.96s,与conda-Faiss-GPU的数据接近了。
所以可以认为这里的时间数据与多核并行运算有关系。

注意事项

恢复安装环境

由于此前已经通过Anaconda安装过Faiss的发行版,为了避免冲突,应将原先的conda的环境变量移除,使得原Faiss环境不可用。

打开环境配置文件,注释conda部分,如下:

vim .bashrc

#####将下列内容注释掉#####

# >>> conda initialize >>>
# !! Contents within this block are managed by 'conda init' !!
__conda_setup="$('/home/xxx/anaconda2/bin/conda' 'shell.bash' 'hook' 2> /dev/null)"
if [ $? -eq 0 ]; then
    eval "$__conda_setup"
else
    if [ -f "/home/xxx/anaconda2/etc/profile.d/conda.sh" ]; then
        . "/home/xxx/anaconda2/etc/profile.d/conda.sh"
    else
        export PATH="/home/xxx/anaconda2/bin:$PATH"
    fi
fi
unset __conda_setup
# <<< conda initialize <<<

重启系统,验证conda是否已经从环境变量中移除

reboot

echo $PATH
conda    # 系统应无此命令
python import faiss  # 操作失败

上述操作只是将conda以及基于Anaconda安装的Faiss库等从环境变量中移除了,但是这些文件本身还在,恢复.bashrc文件,可以将它们从系统中恢复过来。

你可能感兴趣的:(Faiss)