- PostgreSQLは、リレーショナルデータベースとして広く利用されているというのは周知の事実。
意外と知られていないのが最近ではベクトル検索の機能も強化されているということ。
いろんな諸事情考えると割と使い道の多いPostgreSQLのRAGを紹介。
なぜローカルのRAGなのか。
個人または企業内の業務効率化のためのRAGアプリなどを作る際にシンプルなベクトル検索だけでいいのにクラウドサービスを使うのはいろいろな事情があって面倒くさいことがある。
個人ならコスト面、企業内であれば新しいリソースを作る必要があるので社内ルールに則った申請や機密情報の取り扱いとして問題がないかどうかの確認などになる。
LLMはさすがにキーとエンドポイントがあれば使えるからよい。
ローカルでやろうにもハードウェアの要件も高いし。
でも、vectorDBの方はわざわざクラウドでやる必要ある?というユースケースもそれなりに経験上あった。私は申請とかが面倒くさいのでローカルで実装しきって乗り切ってしまったことが多い。
意外と役に立ったケースが多いので備忘兼共有用として以下にPostgreSQLを使ったRAGの実装を紹介する。
なぜPostgreSQLなのか
「ただローカルでRAGするだけならlangchainのvectorstoresとかがあるじゃない」というのが知ってる人からはよく指摘される。
でも実際私がPostgreSQL推しなのは
- やっぱRDBだから。
- しかもローカルだとストレージが吹っ飛ぶまで使えなくなることがほぼないのが良い。(可用性が高い)
- langchainのvectorstoresのようにローカルファイルに書いておいておくだけだと移行とか万が一データが吹っ飛んだら割とおしまいだが、RDBなら定期的にバックアップしといてrestoreすれば良いから全体的に保全性が高い
- クラウドに置かないので「うっかり誰でも見れるところにデータを置いちゃった」がほぼ起こり得ないので安全性も高い
- そしてRDBのなかでも実績があるので運用面で信頼できる。
の2点がでかい。
結局企業なら、クラウドにおいてしまっても社内ルール的にokか、法的にokか、各種契約的にokかと気にしなければならないことたくさんです。
個人なら、Azure AISearchはストレージよりはだいぶ高いのでケチっていきたい
なので「ちょうどいい中間択」になりやすい。
※ベクトル検索とは、テキストをベクトルとして表現し、類似性を基に検索を行う手法です。この手法を用いることで、従来のキーワード検索では難しい、意味的な類似性を考慮した検索が可能になる。
環境構築
本記事の割とクライマックス。condaの環境では各種ライブラリを動かすために必要なexeファイルなどまで付いてくることが多い。これが非常に助かる。
企業とかでは勝手にソフトウェアのインストールができなかったり、怒られたりするが、流石にPythonやminicondaを止められてるケースは少ないので抜け穴的に環境構築できたりする。
# 仮想環境の作成
conda create -n local_ragenv -c conda-forge python=3.11 openai postgresql pgvector sqlalchemy
# 仮想環境の有効化
conda activate local_ragenv
# user直下にデータベース作成・起動
initdb $HOME/vector_store
pg_ctl start -D $HOME/vector_store
psql -c "create database ragdb"
psql -c "CREATE EXTENSION IF NOT EXISTS vector"
PostgereSQLへのvector格納と実装
PostgreSQLでのベクトル検索を実装するためには、まずデータをベクトル形式で格納する必要があります。これには、PostgreSQLの拡張機能であるpgvectorを利用して実現する。pgvectorは、ベクトルデータ型を提供し、ベクトル間の距離計算を効率的に行うことができます。まず、pgvectorをインストールし、データベースに拡張機能を追加します。次に、ベクトルを格納するためのテーブルを作成し、必要なカラムを定義します。例えば、テキストデータを埋め込むために、事前にトレーニングされたモデルを使用して、各テキストをベクトルに変換し、その結果をテーブルに挿入します。
データがベクトル形式で格納されたら、次は検索機能を実装します。PostgreSQLでは、ユークリッド距離やコサイン類似度など、さまざまな距離計算を用いてベクトル間の類似性を評価できます。これにより、特定のクエリベクトルに対して、最も類似したベクトルを持つデータを効率的に取得することが可能です。具体的には、SELECT文を使用して、特定のベクトルとの距離が最小となるデータを取得するクエリを作成します。この際、ORDER BY句を用いて距離の昇順にソートし、LIMIT句で取得する件数を制限することで、上位の類似データを簡単に取得できます。
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from pgvector.sqlalchemy import Vector
import openai
# ベクトル化関連
openai.api_key = 'your_openai_api_key'
def generate_embeddings(chunk):
embeddings = []
response = openai.Embedding.create(
input=chunk,
model="text-embedding-ada-002"
)
embeddings.append(response['data'][0]['embedding'])
return embeddings
# データベース接続の設定
DATABASE_URL = "postgresql+psycopg2://localhost:5432/vector_store"
# データベースエンジンの作成
engine = create_engine(DATABASE_URL)
# ベースクラスの定義
Base = declarative_base()
# Userテーブルの定義
class docments(Base):
__tablename__ = 'TextEmbedding'
id = Column(Integer, primary_key=True) # 適当に作る(category_detailの組でそうなるならそれでも良い)
category = Column(String(50), nullable=False)
detail = Column(String(500))
vector = mapped_column(Vector(1536)) # 使うエンジンの次元数に合わせるtext-embedding-ada2は1536
# テーブル作成
Base.metadata.create_all(engine)
# セッションの作成
Session = sessionmaker(bind=engine)
session = Session()
# ---------------------
# CRUD操作の例
# ---------------------
# CREATE(ドキュメントの作成)
def create_document(category, detail, vector):
new_document = Document(category=category, detail=detail, vector=vector)
session.add(new_document)
session.commit()
return new_document
# READ(ドキュメントの取得)
def get_documents():
documents = session.query(Document).all()
return documents
# UPDATE(ドキュメントの更新)
def update_document(document_id, category=None, detail=None, vector=None):
document = session.query(Document).filter_by(id=document_id).first()
if document:
if category:
document.category = category
if detail:
document.detail = detail
if vector is not None:
document.vector = vector
session.commit()
return document
# DELETE(ドキュメントの削除)
def delete_document(document_id):
document = session.query(Document).filter_by(id=document_id).first()
if document:
session.delete(document)
session.commit()
return document
# ---------------------
# CRUD操作の実行例
# ---------------------
# ドキュメントの作成
new_document = create_document("example_category", "This is an example detail.", generate_embeddings("任意のコンテンツ"))
# text-embeddingで用意したvectorを入れること。
print("Created Document:", new_document.category, new_document.detail)
# 類似度>0/7かつ上位5件のrecordを取得
def find_similar_embeddings(query_embedding, limit=5):
k = 5
similarity_threshold = 0.7
query = session.query(TextEmbedding, TextEmbedding.embedding.cosine_distance(query_embedding)
.label("distance"))
.filter(TextEmbedding.embedding.cosine_distance(query_embedding) < similarity_threshold)
.order_by("distance")
.limit(k)
.all()
# セッションを閉じる
session.close()
SQL alchemyでやりましたが、psycopg2とかでも全然良いです。SQLかける人ならそっちのほうが多分楽です。
補足_RAGアプリにおけるPostgreSQLの利点と活用事例
PostgreSQLは、リレーショナルデータベース管理システムとして広く利用されており、その柔軟性と拡張性から、RAG(Retrieval-Augmented Generation)アプリケーションにおいても非常に有用です。特に、PostgreSQLは、データの整合性を保ちながら、複雑なクエリを効率的に処理できるため、情報の取得と生成を組み合わせたアプリケーションにおいて強力な選択肢となります。RAGアプリでは、ユーザーのクエリに対して関連する情報を迅速に取得し、その情報を基に生成された応答を提供することが求められます。このプロセスにおいて、PostgreSQLの特性が大いに役立ちます。
まず、PostgreSQLのフルテキスト検索機能は、RAGアプリにおける情報検索の精度を向上させる要素の一つです。ユーザーが入力したクエリに対して、関連する文書やデータを迅速に検索し、必要な情報を抽出することが可能です。これにより、ユーザーは求める情報に素早くアクセスでき、アプリケーションの利便性が向上します。また、PostgreSQLはJSONBデータ型をサポートしており、非構造化データを効率的に扱うことができます。これにより、さまざまな形式のデータを統合し、RAGアプリの情報源として活用することができます。
さらに、PostgreSQLは拡張性が高く、ユーザーが独自の関数やデータ型を追加することができます。これにより、特定のニーズに応じたカスタマイズが可能となり、RAGアプリの機能を強化することができます。たとえば、特定のドメインに特化した情報を扱う場合、独自の検索アルゴリズムやデータ処理ロジックを実装することで、より精度の高い情報提供が実現できます。このような柔軟性は、競争の激しい市場において差別化要因となるでしょう。
実際の活用事例としては、教育分野におけるRAGアプリが挙げられます。学生が特定のトピックについて質問をすると、PostgreSQLを用いて関連する教材や参考文献を検索し、その情報を基に生成された回答を提供することができます。このプロセスにより、学生は必要な情報を迅速に得ることができ、学習効率が向上します。また、企業内のナレッジベースを構築する際にも、PostgreSQLを利用することで、社内文書やFAQから関連情報を抽出し、従業員が必要な情報にアクセスしやすくすることが可能です。
このように、PostgreSQLはRAGアプリにおいて多くの利点を提供します。情報の整合性を保ちながら、迅速かつ効率的にデータを処理できるため、ユーザーにとって価値のある体験を提供することができます。したがって、RAGアプリの実装においてPostgreSQLを選択することは、非常に理にかなった選択と言えるでしょう。
補足_他のベクトル検索エンジンとの比較と選択基準
今回選定していないが他にもベクトル検索のエンジンはあるので記載だけをば。私自身実装したことはないので注意されたい。
PostgreSQLを使用したローカルのRAGアプリの実装において、ベクトル検索エンジンの選択は非常に重要です。特に、Azure AI Search以外の選択肢を考慮する際には、さまざまな要因を比較し、最適なエンジンを選ぶ必要があります。まず、ベクトル検索エンジンの基本的な機能を理解することが重要です。これにより、どのエンジンが特定のニーズに最も適しているかを判断できます。
例えば、Elasticsearchは非常に人気のある選択肢であり、特にフルテキスト検索に強みを持っています。Elasticsearchは、スケーラビリティとパフォーマンスに優れ、リアルタイムでのデータ分析が可能です。さらに、豊富なプラグインとエコシステムがあり、さまざまなユースケースに対応できます。しかし、Elasticsearchは設定が複雑で、特に初めて使用する場合には学習曲線が急になることがあります。この点を考慮すると、シンプルさを重視する場合には他の選択肢も検討する価値があります。
次に、PineconeやWeaviateといった新しいベクトル検索エンジンも注目されています。これらのエンジンは、特に機械学習モデルとの統合が容易で、ベクトルデータの管理が効率的です。Pineconeは、スケーラブルなベクトルデータベースとして設計されており、リアルタイムでの検索が可能です。一方、Weaviateは、セマンティック検索に特化しており、知識グラフとの統合が強力です。これにより、より高度な検索機能を実現できますが、特定のユースケースにおいては、他のエンジンよりもパフォーマンスが劣る場合もあります。
また、FAISS(Facebook AI Similarity Search)も選択肢の一つです。FAISSは、特に大規模なデータセットに対して高速な近似最近傍検索を提供します。これは、特に機械学習やデータサイエンスの分野での利用が期待されており、GPUを活用することでパフォーマンスを大幅に向上させることができます。しかし、FAISSは主にライブラリとして提供されているため、他のエンジンと比較して実装の手間がかかることがあります。
選択基準としては、まずはプロジェクトの要件を明確にすることが重要です。データの規模、検索の速度、リアルタイム性、そしてシステムの複雑さなど、さまざまな要因を考慮する必要があります。さらに、コミュニティのサポートやドキュメントの充実度も重要なポイントです。これにより、問題が発生した際に迅速に解決できる可能性が高まります。
最終的には、各エンジンの特性を理解し、自分のプロジェクトに最も適したものを選ぶことが成功の鍵となります。PostgreSQLと組み合わせることで、強力なRAGアプリを構築するための基盤を整えることができるでしょう。選択肢は多岐にわたりますが、しっかりとした比較と検討を行うことで、最適なベクトル検索エンジンを見つけることができるはずです。PostgreSQLによるローカルのRAGアプリ実装において、Azure AI Search以外のベクトル検索の選択肢としては、以下のようなものがあります。