1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

自宅PCの Ubuntu にベクトルデータベース(faiss)を構築

Last updated at Posted at 2025-04-06

はじめに

自宅PCにベクトルデータベース(faiss)を構築しました。諸事情により、Ubuntu Server を使用します。Ubuntu の GUI は使用せず、他のPCの Windows から接続して操作します。

コマンド実行に PuTTY を使用します。今回は使用していませんが、 Tunnels を設定すると、Windows から VNC を使って接続することもできます。
PuTTY 上で文字をマウスで選択すると、自動的にコピーされ、他のアプリケーションに貼り付けることができます。また、PuTTY 上でマウスの右クリックを行うと、その位置にクリップボードの内容が貼り付けられます。
https://www.putty.org/

0406_05.png

ファイルのダウンロードが必要な場合は、Windows のブラウザでファイルをダウンロードし、WinSCP を使用して Ubuntu Server に転送します。

https://winscp.net/eng/download.php

0406_06.png

NVIDIA CUDA のインストールはこちらを参考にしました。

ローカル LLM の環境構築はこちらです。

開発環境

デスクトップPC(ASRock X300 マザーボード)
AMD Ryzen 5 5600G 6コア 12スレッド 3.9GHz
メモリ DDR4-3200 16GB x 2
SSD 1TB
GeForce RTX 3050

 
インストールするツールのバージョンを合わせないと、faiss-gpu が動作しないようです。以下の環境を目指します。

ツール 付属 バージョン
Ubuntu Server 22.04.3
Kernel 6.2.0-26
Default GCC 11.4.0
GLIBC 2.35
NVIDIA Driver 550.54.14
CUDA Toolkit 12.4
faiss-gpu 1.9.0

Ubuntu Server インストール

ubuntu-22.04.3-live-server-amd64.iso

Rufus を使って起動 USB メモリを作成
PCの電源を入れて [F2] または DEL キーを押して USB から立ち上げる
Try or Install Ubuntu Server を選択して、PCへのインストールを開始

Language: English
Keyboard: Japanese
Your name: webmaster
Your servers name: jellyfish01
Pick a username: webmaster
Chose a password: ******
Confirm your password: ******
ネットワークは有線で接続
SSH は後でインストールする
インストールが終わったら、USB メモリを外して起動

 
OS アップデート バージョンを維持するために実施しない
sudo apt-get update
sudo apt-get upgrade
sudo apt-get dist-upgrade
再起動

 
SSH インストール
sudo apt update
sudo apt install openssh-server

外部から SSH で接続  ※ ここから putty で操作できます
ssh -l webmaster 192.168.1.24
password: ******

 
固定IP
ip addr show でネットワークカードの名前を確認(enp2s0)
sudo nano /etc/netplan/80-fixed-ip-address.yaml

80-fixed-ip-address.yaml
network:
    ethernets:
        enp2s0:
            dhcp4: false
            addresses:
                - 192.168.1.13/24
            nameservers:
                addresses: [210.123.12.34,789.12.654.32]
            routes:
                - to: default
                  via: 192.168.1.1
            dhcp6: false
    version: 2

sudo chmod 600 /etc/netplan/80-fixed-ip-address.yaml
sudo netplan apply
※ putty で操作していると、変更直後にネットワークが切断されるので、新しい IP アドレスで再接続します

 
よく使うコマンド
pwd
ls -alF
ip addr show
nano .env (矢印キーでカーソル移動、保存Ctrl+S、終了Ctrl+X)
Ctrl+Alt+[F6] でGUIを抜けてコンソール画面に行く
sudo shutdown -h now
sudo reboot

CUDA Toolkit

https://developer.nvidia.com/cuda-toolkit

旧バージョンは、右下にある「Archive of Previous CUDA Releases」のリンクから入手できます

CUDA Toolkit 12.4
Linux、x86_64、Ubuntu、22.04、deb (local)

wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-ubuntu2204.pin
sudo mv cuda-ubuntu2204.pin /etc/apt/preferences.d/cuda-repository-pin-600
wget https://developer.download.nvidia.com/compute/cuda/12.4.0/local_installers/cuda-repo-ubuntu2204-12-4-local_12.4.0-550.54.14-1_amd64.deb
sudo dpkg -i cuda-repo-ubuntu2204-12-4-local_12.4.0-550.54.14-1_amd64.deb
sudo cp /var/cuda-repo-ubuntu2204-12-4-local/cuda-*-keyring.gpg /usr/share/keyrings/sudo apt-get update
sudo apt-get -y install cuda-toolkit-12-4

ここで、5行目は、4行目を実行して「To install the key, run this command:」で表示されるコマンドに置き換えて実行します

sudo apt-get install -y cuda-drivers

再起動

nano ~/.bashrc

~/.bashrc
export PATH="/usr/local/cuda/bin:$PATH"
export LD_LIBRARY_PATH="/usr/local/cuda/lib64:$LD_LIBRARY_PATH"

source ~/.bashrc

nano ~/.profile

~/.profile
#!/bin/bash
if [[ -f ~/.bashrc ]] ; then
    .  ~/.bashrc
fi

nvidia-smi

Driver Version: 550.54.14

nvcc -V

nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2024 NVIDIA Corporation
Built on Tue_Feb_27_16:19:38_PST_2024
Cuda compilation tools, release 12.4, V12.4.99
Build cuda_12.4.r12.4/compiler.33961263_0

 
以下のウェブページを参考に、すべての nvidia のサービスが enable になっていることを確認します

sudo systemctl list-unit-files --type=service

必要ならば以下を実行して enable にします
sudo systemctl enable nvidia-hibernate.service nvidia-persistenced.service nvidia-resume.service nvidia-suspend.service

Anaconda

https://www.anaconda.com/download

ホームページでメールを登録すると、ダウンロードリンクが届きます
Linux、Python 3.12
Anaconda3-2024.10-1-Linux-x86_64.sh
をダウンロード。home ディレクトリにファイルを移動
bash ~/Anaconda3-2024.10-1-Linux-x86_64.sh
./anaconda3/bin/conda init
次のターミナルの起動から、conda 環境(base)になります

解除は conda deactivate

ベクトルデータベース(faiss)のインストール

事前準備

sudo apt install unzip

faiss-gpu インストール

conda install -c pytorch -c nvidia faiss-gpu=1.9.0

動作確認のために、faiss のページにある DOWNLOAD ZIP からプログラムコード一式をダウンロードします

unzip faiss-main.zip
cd faiss-main/tutorial/python

コンソール
(base) webmaster@jellyfish01:~/faiss-main/tutorial/python$ ls -1
1-Flat.py
2-IVFFlat.py
3-IVFPQ.py
4-GPU.py
5-Multiple-GPUs.py
7-PQFastScan.py
8-PQFastScanRefine.py
9-RefineComparison.py
実行
python 1-Flat.py
結果
True
100000
[[  0 393 363  78]
 [  1 555 277 364]
 [  2 304 101  13]
 [  3 173  18 182]
 [  4 288 370 531]]
[[0.        7.1751738 7.20763   7.2511625]
 [0.        6.3235645 6.684581  6.799946 ]
 [0.        5.7964087 6.391736  7.2815123]
 [0.        7.2779055 7.5279875 7.662846 ]
 [0.        6.7638035 7.2951202 7.3688145]]
[[ 381  207  210  477]
 [ 526  911  142   72]
 [ 838  527 1290  425]
 [ 196  184  164  359]
 [ 526  377  120  425]]
[[ 9900 10500  9309  9831]
 [11055 10895 10812 11321]
 [11353 11103 10164  9787]
 [10571 10664 10632  9638]
 [ 9628  9554 10036  9582]]

実行
python 2-IVFFlat.py
結果
[[ 9900  9309  9810 10048]
 [11055 10895 10812 11321]
 [11353 10164  9787 10719]
 [10571 10664 10632 10203]
 [ 9628  9554  9582 10304]]
[[ 9900 10500  9309  9831]
 [11055 10895 10812 11321]
 [11353 11103 10164  9787]
 [10571 10664 10632  9638]
 [ 9628  9554 10036  9582]]

実行
python 3-IVFPQ.py
結果
[[   0   78  608  159]
 [   1  555 1063    5]
 [   2  304  179  134]
 [   3  265  182  527]
 [   4  288  531  827]]
[[1.5882578 6.3516216 6.516344  6.597596 ]
 [1.2973676 5.735963  5.951533  6.3694015]
 [1.7135715 5.651118  6.1645284 6.3573594]
 [1.8189969 6.7453256 7.0173464 7.044258 ]
 [1.4755101 5.778677  6.308148  6.4126477]]
[[10242  9900  8746 10914]
 [10913 10765 10473 11373]
 [10719 11291  9380 10600]
 [10630 10664 10122 10005]
 [ 9229  9878 10304  9459]]

実行
python 4-GPU.py
結果
100000
[[ 381  207  210  477]
 [ 526  911  142   72]
 [ 838  527 1290  425]
 [ 196  184  164  359]
 [ 526  377  120  425]]
[[ 9900 10500  9309  9831]
 [11055 10895 10812 11321]
 [11353 11103 10164  9787]
 [10571 10664 10632  9638]
 [ 9628  9554 10036  9582]]
100000
[[ 381  207  210  477]
 [ 526  911  142   72]
 [ 838  527 1290  425]
 [ 196  184  164  359]
 [ 526  377  120  425]]
[[ 9900  9309  9810 10048]
 [11055 10895 10812 11321]
 [11353 10164  9787 10719]
 [10571 10664 10632 10203]
 [ 9628  9554  9582 10304]]

実行
python 5-Multiple-GPUs.py
結果
number of GPUs: 1
100000
[[ 381  207  210  477]
 [ 526  911  142   72]
 [ 838  527 1290  425]
 [ 196  184  164  359]
 [ 526  377  120  425]]
[[ 9900 10500  9309  9831]
 [11055 10895 10812 11321]
 [11353 11103 10164  9787]
 [10571 10664 10632  9638]
 [ 9628  9554 10036  9582]]

実行
python 7-PQFastScan.py
結果
[[0 1 2 3]
 [0 1 2 3]
 [0 1 2 3]
 [0 1 2 3]
 [0 1 2 3]]
[[16.33721  16.33721  16.33721  16.33721 ]
 [15.116527 15.116527 15.116527 15.116527]
 [17.16953  17.16953  17.16953  17.16953 ]
 [17.143526 17.143526 17.143526 17.143526]
 [16.08081  16.08081  16.08081  16.08081 ]]
[[6742 6788 6802 6803]
 [6742 6788 6802 6803]
 [6742 6788 6802 6803]
 [6742 6788 6802 6803]
 [6742 6788 6802 6803]]

実行
python 8-PQFastScanRefine.py
結果
[[13 18 24  9 17  2 11 26 19  7]
 [19 14 22 18 21 29 27  4 13 25]
 [18 13 26 23  2  9 11 19  0  4]
 [10 11 13 26 12 20  4 18  0 15]
 [ 4 13 23  3  0 19 14 29 18 22]]
[[ 9.544294   9.606979   9.746547   9.758554  10.249306  10.555075
  10.558601  10.6802635 10.904479  11.284525 ]
 [ 8.090923   8.267822   8.660107   9.150578   9.263431   9.5033
   9.573061  10.001304  10.151635  10.272773 ]
 [ 8.490352   8.556078   8.782871   9.400972   9.495619   9.554632
   9.803928   9.993049  10.504381  10.699816 ]
 [ 8.330563   8.555349   8.671155   9.483734   9.542344   9.554526
   9.724651   9.920914   9.926396  10.169458 ]
 [ 8.472397   8.755564   8.837374   8.9055195  9.545532   9.992572
  10.073465  10.271679  10.275333  10.508036 ]]
[[0 1 2 3]
 [0 1 2 3]
 [0 1 2 3]
 [0 1 2 3]
 [0 1 2 3]]

実行
python 9-RefineComparison.py
結果
[[37127 58750 80258 23369 15585 76780   715 47582 36997 75695 79478  2731
  37226 37030  1461 14625 25695 72008 51978  3029 52360 34597 49870 99230
  82166 61145 77554 41486 52938 45992 31675 83963 82884 51824 14253 26829
  98367 47779 16280  5701 38769 37444 40950  3154 75185 81630 11084 34055
  17043 57268 37109 85484 47040 37074 36626 13266 59875 87351 51478 42139
  11844 37427  8318 21919 65728 15532 72959 17458 31988 43550 87598 95532
  87899 48962 52356 37218  3786 39929 72032 30351  2316 37318 21947 85248
  81821 65540 35176 67053 60871 83799 85489 45245 50618 79618 57931 21242
    119  9385 71709 18979]
 [  736 25457 73476 44125 40255 49243  1787 32306 68735 80902 86966 77102
  41056 69383 41204 49796  1248 61255  7709 91908 83172 60422 10562 15626
...

 

PostgreSQL インストール

sudo apt update
sudo apt install postgresql postgresql-contrib
psql --version

sudo -i -u postgres
psql
postgres=#

CREATE USER admin WITH PASSWORD 'mypassword' SUPERUSER;
CREATE USER webmaster WITH PASSWORD 'mypassword';

CREATE DATABASE webmaster OWNER webmaster;

\q
で抜ける
exit
で postgres ユーザーを抜ける

psql -U webmaster -d webmaster -h localhost

CREATE TABLE knowledge_documents (
    id UUID PRIMARY KEY,
    knowledge_id TEXT NOT NULL,
    content TEXT NOT NULL,
    title TEXT NOT NULL,
    metadata JSONB,
    created_at TIMESTAMP DEFAULT now()
);

\q
で抜ける

\d knowledge_documents  (テーブル一覧)
\l  (データベース一覧)
\c  (現在接続しているデータベース)
SELECT * FROM knowledge_documents;

faiss_service の作成

次のように作成します。

項目 ツール
文書データベース PostgreSQL
ベクトルデータベース・検索 faiss-gpu
埋め込み(Embeddings) OpenAI API(text-embedding-3-small)
ウェブサーバー Python Flask
pip install flask psycopg2 openai

mkdir faiss_service
cd faiss_service
nano faiss_service.py

faiss_service.py (1/7)
from flask import Flask, request, jsonify
from openai import OpenAI
import psycopg2
import faiss
import numpy as np
import os
import uuid
import json

app = Flask(__name__)
client = OpenAI()
faiss_service.py (2/7)
# DB接続設定
db_conn = psycopg2.connect(
    dbname="webmaster",
    user="webmaster",
    password=os.environ.get("PSQL_DB_PASSWORD"), # 環境変数から取得
    host="localhost",
    port="5432"
)
faiss_service.py (3/7)
# FAISSインデックスの初期化
dimension = 1536
index_path = "faiss.index" # knowledge が複数ある場合は、複数 faiss_service_n.py を作成して、ポートで分ける(faiss.index.n)
res = faiss.StandardGpuResources()  # use a single GPU
doc_id_map = []

if os.path.exists(index_path):
    index = faiss.read_index(index_path)
    gpu_index = faiss.index_cpu_to_gpu(res, 0, index)
    with open("doc_id_map.json", "r") as f:
        doc_id_map = json.load(f)
    print("FAISSインデックスをロードしました")
else:
    index = faiss.IndexFlatL2(dimension)
    gpu_index = faiss.index_cpu_to_gpu(res, 0, index)
    print("FAISS新規インデックスを作成しました")

def save_faiss():
    cpu_index = faiss.index_gpu_to_cpu(gpu_index)
    faiss.write_index(cpu_index, index_path)
    with open("doc_id_map.json", "w") as f:
        json.dump(doc_id_map, f)
    print("FAISSインデックスを保存しました")
faiss_service.py (4/7)
@app.route('/retrieval', methods=['POST'])
def retrieval():
    """クエリベクトルで検索"""
    try:
        data = request.get_json()
        knowledge_id = data.get("knowledge_id") # knowledge が複数ある場合は、複数 faiss_service_n.py を作成して、ポートで分ける
        query = data.get("query")  # ユーザーが送信した質問文
        retrieval_setting = data.get("retrieval_setting", {})
        top_k = retrieval_setting.get("top_k", 5)
        score_threshold = retrieval_setting.get("score_threshold", 0.5)

        if not knowledge_id:
            return jsonify({"error_code": 1001, "error_msg": "knowledge_idは必須です"}), 400

        if not query:
            return jsonify({"error_code": 1002, "error_msg": "queryは必須です"}), 400

        # OpenAI API でクエリをベクトル化
        response = client.embeddings.create(
            input=query,
            model="text-embedding-3-small"
        )
        query_vector = np.array(response.data[0].embedding, dtype=np.float32)

        # FAISS検索
        D, I = gpu_index.search(np.array([query_vector]), top_k)

        cur = db_conn.cursor()

        records = []
        for dist, idx in zip(D[0], I[0]):
            if idx == -1:
                continue
            score = 1 / (1 + dist)
            if score < score_threshold:
                continue

            # UUIDを復元
            doc_uuid = doc_id_map[idx]

            # PostgreSQLから取得
            cur.execute("""
                SELECT content, title, metadata
                FROM knowledge_documents
                WHERE id = %s AND knowledge_id = %s
            """, (doc_uuid, knowledge_id))
            row = cur.fetchone()
            if row:
                records.append({
                    "content": row[0],
                    "title": row[1],
                    "metadata": row[2],
                    "score": score
                })

        return jsonify({"records": records})

    except Exception as e:
        return jsonify({"error_code": 5000, "error_msg": str(e)}), 500
faiss_service.py (5/7)
@app.route('/add', methods=['POST'])
def add():
    """新しいドキュメント追加"""
    try:
        data = request.get_json()
        knowledge_id = data['knowledge_id'] # knowledge が複数ある場合は、複数 faiss_service_n.py を作成して、ポートで分ける
        content = data['content']
        title = data['title']
        metadata = data.get('metadata', {})

        # OpenAI APIで埋め込みベクトルを取得
        response = client.embeddings.create(
            input = content,
            model = "text-embedding-3-small"
        )
        vector = np.array(response.data[0].embedding, dtype=np.float32)
        
        # UUID生成
        new_id = str(uuid.uuid4())

        # PostgreSQLに保存
        cur = db_conn.cursor()
        cur.execute("""
            INSERT INTO knowledge_documents (id, knowledge_id, content, title, metadata)
            VALUES (%s, %s, %s, %s, %s)
        """, (new_id, knowledge_id, content, title, json.dumps(metadata)))
        db_conn.commit()

        # FAISSに保存
        gpu_index.add(np.array([vector]))
        doc_id_map.append(new_id)
        save_faiss()

        return jsonify({"status": "success", "id": new_id})

    except Exception as e:
        return jsonify({"error_code": 5000, "error_msg": str(e)}), 500
faiss_service.py (6/7)
@app.route('/delete', methods=['POST'])
def delete():
    """ドキュメントを削除"""
    try:
        # すべての文書を再度ベクトル化するため、コストがかかります
        
        data = request.get_json()
        doc_id = data['id']  # 削除対象のUUID文字列
        # knowledge が複数ある場合は、複数 faiss_service_n.py を作成して、ポートで分ける

        # PostgreSQLから削除
        cur = db_conn.cursor()
        cur.execute("DELETE FROM knowledge_documents WHERE id = %s", (doc_id,))
        db_conn.commit()

        # 関数内で値を変更しする際は、global宣言が必要
        global gpu_index
        global doc_id_map

        # FAISSを一旦すべて削除
        dimension = 1536
        index = faiss.IndexFlatL2(dimension)
        gpu_index = faiss.index_cpu_to_gpu(res, 0, index)
        doc_id_map = []

        # PostgreSQLからデータ読み出してFAISS再構築
        cur.execute("SELECT id, content FROM knowledge_documents")
        rows = cur.fetchall()

        for row in rows:
            doc_uuid, content = row

            # OpenAI APIで再度ベクトル化
            response = client.embeddings.create(
                input=content,
                model="text-embedding-3-small"
            )
            vector = np.array(response.data[0].embedding, dtype=np.float32)

            # FAISSに追加
            gpu_index.add(np.array([vector]))
            doc_id_map.append(doc_uuid)

        # 保存
        save_faiss()
        
        return jsonify({"status": "success", "id": doc_id})

    except Exception as e:
        return jsonify({"error_code": 5000, "error_msg": str(e)}), 500
faiss_service.py (7/7)
# ウェブサーバー起動
@app.route("/")
def index():
    return "こんにちは、これは Python の Web サーバーです!"

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000)
実行
export PSQL_DB_PASSWORD="..."
export OPENAI_API_KEY="..."
python faiss_service.py

0428_05m.png

Dify に接続

Dify で外部ナレッジベースの設定を行って、faiss_service に接続します。

ナレッジ
「外部ナレッジベースと連携 →」

外部ナレッジベース連携APIを追加

API Key は未使用なので ABCdef など

外部ナレッジベース名: ローカルRAG
外部ナレッジベースID: AAA-BBB-CCC

「連携 →」ボタン押下

 

 

参考ページ

【Python】faiss-gpu をビルドしてインストールする

FAISS Getting started

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?