ベクトル検索の使用法
ベクトル検索は、機械学習および人工知能を使用するシステムにとって基本となる概念です。埋め込みモデルを使用して、テキストやイメージなどの構造化されていないデータを埋め込みベクトルに変換した後、ユーザはこれらのベクトルに対して演算を実行して入力を処理し、意味的に最も類似するベクトルを返すことができます。
InterSystems IRIS SQL では、ベクトルをコンパクトで高性能の VECTOR 型と EMBEDDING 型で格納できるため、ベクトル化したデータを従来のリレーショナル・スキーマの一部として効率的に格納できます。EMBEDDING 型を活用することで、InterSystems IRIS SQL は、埋め込みモデルと直接やり取りすることなく、使い慣れた SQL 構文によって、テキストを埋め込みベクトルに変換します。
ベクトルと埋め込み
ベクトルは、埋め込みにおいて意味論的意味を表すために使用できます。これらの埋め込みは、テキスト、イメージ、または音声を高次元の幾何学的空間にマッピングする機械学習モデルである埋め込みモデルによって決定されます。このページでは、特にテキストによる埋め込みモデルの使用について説明しますが、ここに示す手順は、さまざまなタイプのデータに対して使用できます。
最新の埋め込みベクトルは通常、数百から数千もの次元に及びます。同様の意味論的意味を持つ単語はその空間内の近い位置を占め、異なる意味論的意味を持つ語句は互いに離れた位置を占めます。こういった空間位置により、アプリケーションでは、埋め込みベクトルでの 2 つの単語や文の間の距離を計算することで、それらの類似性をアルゴリズムで判定することが可能になります。
ベクトル検索では、ドット積など、距離関数を使用して、入力ベクトルとデータベースに格納されているベクトルとを比較します。ベクトル検索によって、意味論的に入力に最も似ているテキストの部分をアルゴリズムで判定できます。このため、ベクトル検索は、情報の取得が含まれるタスクに最適です。
InterSystems IRIS SQL は、SIMD CPU 命令を活用して距離計算を効率的に実行する専用の VECTOR 型と、VECTOR 型のすべての最適化を使用しつつ、同じテーブル内のソース・フィールド (通常は VARCHAR 型) から埋め込みベクトルへの文字列値の変換を簡素化する EMBEDDING 型をサポートしています。数値ベクトル型には、double型 (埋め込みで広く使用)、decimal 型、integer 型の 3 種類があります。
VECTOR と EMBEDDING は、標準の SQL データ型であるため、他のデータと共にリレーショナル・テーブルに格納して、SQL データベースを透過的にハイブリッドのベクトル・データベースに変換できます。INSERT 文で VECTOR 型データを挿入するには、TO_VECTOR 関数を使用します。この関数を使用するには、手動で埋め込みを作成してから、これを INSERT 文に含める必要があることに注意してください。EMBEDDING 型データを挿入するには、埋め込み構成を定義してから、INSERT 文を使用して EMBEDDING 列のソース・フィールドに文字列を挿入します。使いやすい EMBEDDING 型の使用をお勧めします。
VECTOR 型データの挿入
テキストの埋め込みへの変換
InterSystems IRIS で埋め込みをベクトルとして格納する前に、まずソースからそれらの埋め込みを作成する必要があります。一般には、4 つのステップでテキストを埋め込みに変換できます。
-
テキストを一連の埋め込みに変換するために使用するパッケージをインポートします。
-
選択した埋め込みモデルの入力仕様に合わせて、テキストを事前に処理します。
-
選択したパッケージのワークフローを使用して、モデルをインスタン化し、テキストを埋め込みに変換します。
組み込み Python を使用すると、(任意のパッケージを使用して) テキストを埋め込みに変換する Python コードと共に、それらの埋め込みをデータベースに直接挿入できる ObjectScript コードを実行できます。Python パッケージを InterSystems IRIS にインポートする方法の詳細は、"Python パッケージのインストールおよびインポート" を参照してください。
例 : 組み込み Python を使用した埋め込みの作成
以下の例では、組み込みの Python リストとして渡された文の入力リストを取得して、それらを sentence_transformers パッケージを使用して埋め込みに変換し、後でテーブルに挿入できるよう埋め込みのリストを返します。埋め込みの挿入の詳細は、"INSERT の実行" を参照してください。この例では、入力文は埋め込みモデルの仕様に合うように、事前に処理されていることが前提となっています。
ClassMethod GetEmbeddingPy(sentences) [ Language = python ]
{
import json
# import the package
import sentence_transformers
# perform any preprocessing
# create the model and form the embeddings
model = sentence_transformers.SentenceTransformer('all-MiniLM-L6-v2')
embeddings = model.encode(sentences)
return embeddings
}
INSERT の実行
埋め込みを表す文字列のリストを用意したら、INSERT 文を使用することによって、またはオブジェクトを作成してそのオブジェクトのプロパティとして埋め込みを格納することによって、それらの埋め込みを VECTOR としてテーブルに挿入できます。以下の例は、INSERT を使用してデータを挿入する方法を示しています。
例
埋め込みごとに、埋め込みを目的のテーブルに追加する INSERT 文を実行します。TO_VECTOR を使用して、埋め込みの文字列表現を VECTOR に変換します。
以下のコマンドでは、1 つの埋め込みが Sample.Description という名前のテーブルに挿入されます。このテーブルには、2 つの列が含まれており、1 つがテキストの記述を表す埋め込み用で、もう 1 つが、埋め込みをその派生元のテキストにリンクするために使用できる一意の識別子用です (このテキストは、同じ識別子と共に別のテーブルに暗黙的に格納されます)。(この例では、埋め込みと一意の識別子のプレースホルダとして ? が使用されています。これらは通常、文のパラメータとしてプログラムで指定されるためです。)
INSERT INTO Sample.Descriptions (DescriptionEmbedding, UID)
VALUES (TO_VECTOR(?,double), ?)
以下のコード・サンプルでは、このクエリを使用して、1 つの埋め込みをテーブルに挿入します。
ClassMethod InsertEmbeddings(embedding As %String, uid As %Integer)
{
set sc=$$$OK
try {
set myquery = "INSERT INTO Sample.Descriptions (DescriptionEmbedding, UID)"
_"VALUES (TO_VECTOR(?,double), ?)"
set tStatement = ##class(%SQL.Statement).%New()
$$$ThrowOnError(tStatement.%Prepare(query))
set rset = tStatement.%Execute(embedding, uid)
if (rset.%SQLCODE < 0) {
throw ##class(%Exception.SQL).CreateFromSQLCODE(rset.%SQLCODE,rset.%Message)
}
}
catch e {
set sc = e.AsStatus()
return sc
}
return 0
}
public void InsertEmbeddings(String embeddings, Integer uid) {
try {
// set connection parameters
IRISDataSource ds = new IRISDataSource();
ds.setServerName("127.0.0.1");
ds.setPortNumber(51776);
ds.setDatabaseName("USER");
ds.setUser("_SYSTEM");
ds.setPassword("SYS");
IRISConnection conn = ds.GetConnection();
String sql = "INSERT INTO Sample.Embeddings (Embedding, UID) " +
"VALUES (TO_VECTOR(?,double), ?)";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.SetString(embedding);
pstmt.SetInt(uid);
pstmt.executeUpdate();
pstmt.close();
} catch (Exception ex) {
System.out.println("caught exception: "
+ ex.GetClass().getName() + ": " + ex.GetMessage());
}
}
def insertEmbeddings(embeddings, uid):
// set the connection parameters
conn_string = "localhost:1972/USER"
username = "_system"
password = "SYS"
connection = iris.connect(conn_string, username, password)
cursor = connection.cursor()
try:
sql = "INSERT INTO Sample.Embeddings (Embedding, UID) " +
"VALUES (TO_VECTOR(?,double), ?)"
params = [embeddings,uid]
cursor.execute(sql,params))
cursor.close()
except Exception as ex:
print(ex)
finally:
if cursor:
cursor.close()
if connection:
connection.close()
static void InsertEmbeddings(string emb, Integer uid)
{
// set the connection parameters
String host = "127.0.0.1";
String port = "51776";
String username = "_SYSTEM";
String password = "SYS";
String namespace = "USER";
IRISConnection conn = new IRISConnection();
IRISConnect.ConnectionString = "Server = " + host
+ "; Port = " + port + "; Namespace = " + namespace
+ "; Password = " + password + "; User ID = " + username;
conn.Open();
String sql = "INSERT INTO Sample.Embeddings (Embedding, UID) " +
"VALUES (TO_VECTOR(?,double), ?)";
IRISCommand cmd = new IRISCommand(sql, conn);
cmd.ExecuteNonQuery();
cmd.Dispose();
conn.Close();
}
EMBEDDING 型データの挿入
埋め込み構成の作成
埋め込みをテーブルに挿入する前に、テキストを埋め込みに変換する際に使用する埋め込みモデルを決める必要があります。モデルを選択したら、そのモデルのメタデータを %Embedding.Config テーブルに挿入して、埋め込み構成を作成できます。埋め込み構成は、選択した埋め込みモデルへの API 呼び出しに必要な情報を格納します。
%Embedding.Config テーブルには、以下の 5 つの列があります。
-
Name : モデルの参照に使用される一意の有効な識別子。
-
Configuration : 特定のソースの特定のデータを含む JSON 形式の文字列。
-
EmbeddingClass : %Embedding.Interface を拡張し、エンドポイントから埋め込みを取得するロジックを定義する ObjectScript クラスの名前。InterSystems IRIS には、%Embedding.OpenAI と %Embedding.SentenceTransformers というすぐに使える 2 つのクラスが用意されています。別のソースからの埋め込みモデルを使用するには、%Embedding.Interface クラスを拡張する新しいクラスを手動で定義する必要があります。
-
VectorLength : 埋め込みモデルが返すベクトルの長さ (次元数)。
-
Description : このエンドポイントのオプションの説明。
以下の例では、OpenAI 埋め込みモデルOpens in a new tabの埋め込み構成を %Embedding.Config テーブルに挿入します。
INSERT INTO %Embedding.Config (Name, Configuration, EmbeddingClass, VectorLength, Description)
VALUES ('my-opanai-config',
'{"apiKey":"<api key>",
"sslConfig": "llm_ssl",
"modelName": "text-embedding-3-small"}',
'%Embedding.OpenAI',
1536,
'a small embedding model provided by OpenAI')
EMBEDDING 型の列を持つテーブルの定義
埋め込み構成を %Embedding.Config テーブルに格納すると、この構成を使用して EMBEDDING 型の列を持つテーブルを作成できます。
埋め込み型の列を定義する場合、model パラメータと source パラメータの 2 つの引数を指定する必要があります。model パラメータは、テキストを埋め込みに変換する埋め込み構成の名前です。source パラメータは、埋め込みの計算に使用するクラスのプロパティのコンマ区切りリストです。
以下の例では、4 つの列のテーブルを作成し、そのうちの 2 列で文字列を格納して、残りの 2 列でその文字列の埋め込みを格納します。EMBEDDING 型のどちらのフィールドも、my-openai-config と呼ばれる埋め込み構成を使用します。
CREATE TABLE Embedding.Example (
Description VARCHAR(200),
Name VARCHAR(30),
DescriptionEmbedding EMBEDDING('my-opanai-config','Description'),
NameEmbedding EMBEDDING('my-opanai-config','Name')
)
ベクトル・インデックスの定義
InterSystems IRIS に VECTOR 型でデータを格納した後、格納したベクトルに対して実行される検索の効率を向上させるには、ベクトル・インデックスを定義します。ベクトル化されたデータは、多くの場合、近似最近傍 (ANN) ベクトル・インデックスでインデックス化されます。
標準のベクトル検索では、入力ベクトルとの比較がデータベース内の個々のベクトルに対して行われます。この方法では、全面的に正確な検索が保証されますが、計算効率が低下します。ANN ベクトル・インデックスでは、特殊なグラフ・データ構造を検索する最近傍アルゴリズムを活用して正確な結果を近似し、入力ベクトルと格納されたベクトルとの間で実行される比較演算の回数を制限します。その結果、検索の実行時に、格納されている各ベクトルとの比較は行われず、代わりにグラフを使用して入力ベクトルに近くないベクトルが排除されます。この方法により、特に大量の高次元データを扱う場合に、ベクトル・データベースでの検索パフォーマンスが大幅に向上します。近似によって精度がわずかに低下するものの、それを上回るパフォーマンスの向上が得られます。
クエリ・オプティマイザでは、標準インデックスの場合と同様に、最も効率的なクエリ・プランでは、定義したベクトル・インデックスを使用しないと判断する場合があります。クエリがベクトル・インデックスを使用するかどうかを確認するには、EXPLAIN コマンドでクエリ・プランを調べます。ANN ベクトル・インデックスは、TOP 節と、ベクトル距離関数 (VECTOR_DOT_PRODUCT など) を伴う ORDER BY ... DESC 節の両方を含むクエリでのみ使用して、最も一致するレコードを見つけることができます。一方、WHERE、GROUP BY、JOIN 条件などの他の節を扱う場合は、通常他のインデックス・タイプが使用されます。
Hierarchical Navigable Small Worldインデックス
InterSystems SQL では Hierarchical Navigable Small World (HNSW) インデックスを定義できます。これは、HNSW アルゴリズムOpens in a new tabを使用してベクトル・インデックスを作成します。HNSW は、InterSystems IRIS にネイティブに実装されている効率的な ANN アルゴリズムで、ベクトル検索に使用されます。
HNSW インデックスは、CREATE INDEX 文を使用して定義できます。HNSW インデックスを定義するには、次の要件を満たす必要があります。
-
HNSW インデックスは VECTOR 型または EMBEDDING 型のフィールドに定義され、double 型または decimal 型の固定長になります。
-
インデックスが定義されるテーブルには、ビットマップをサポートする ID が必要です。
-
インデックスが定義されるテーブルでは、既定のストレージを使用する必要があります。
HNSW インデックスを定義する場合、Distance インデックス・パラメータでインデックスが使用する距離関数を指定する必要があります。このパラメータには Cosine と DotProduct の 2 つの値を指定できます。このパラメータは大文字と小文字を区別せず、一重引用符で囲む必要があります。
HNSW アルゴリズムに固有のパラメータがさらに 2 つあり、HNSW インデックスの定義時にオプションで指定できます。
-
M (オプション) : 構築時に新しい要素ごとに作成される双方向リンクの数。この値は 1 より大きい正の整数にします。値の範囲は 2 ~ 100 です。M 値が高いほど、高次元のデータセットや再現性の高いデータセットに適しており、M 値が低いほど、低次元のデータセットや再現性の低いデータセットに適しています。既定値は 16 です。
-
efConstruction (オプション) : 最近傍探索のダイナミック・リストのサイズ。この値は M より大きい正の整数にします。efConstruction の値が大きいほど、一般的にインデックスの品質は向上しますが、構築時間が長くなります。efConstruction には最大値が存在し、その値を超えるとインデックスの品質が向上しなくなります。既定値は 64 です。
以下の例では、パラメータにさまざまな値を指定して HNSW インデックスを定義しています。
CREATE INDEX HNSWIndex ON TABLE Company.People (Biography)
AS HNSW(Distance='Cosine')
CREATE INDEX HNSWIndex ON TABLE Company.People (Biography)
AS HNSW(M=24, Distance='DotProduct')
CREATE INDEX HNSWIndex ON TABLE Company.People (Biography)
AS HNSW(M=32, efConstruction=100, Distance='Cosine')
ベクトル検索の実行
ベクトル検索では、1 つのベクトルを使用して、データベース内に格納されている他の似たベクトルを検索できます。InterSystems SQL では、そのような検索を 1 つの SELECT クエリで実行できます。
現在、InterSystems SQL では、2 つのベクトルの類似性を判定する関数として、VECTOR_DOT_PRODUCT および VECTOR_COSINE の 2 つがサポートされています。これらの関数の値が大きいほど、ベクトルの類似性が高くなります。ORDER BY 節に DESC オプションを指定して使用すると、最も類似するベクトルが結果セットの最上部にくるようにソートされた一連のベクトルが返されます。
ベクトル・インデックスを定義した場合は、ORDER BY 節で DESC オプションを使用して、結果を降順で返すように指定してください。
例
以下の例は、SQL を使用して、VECTOR_DOT_PRODUCT を使用するクエリを発行し、意味論的に入力文に最も似ている記述を検索する方法を示しています。入力した検索語を EMBEDDING 関数で埋め込みに変換し、ORDER BY 節内で VECTOR_DOT_PRODUCT または VECTOR_COSINE を使用して、類似性の特に高いいくつかのテキストを返します。この例では、EMBEDDING 関数はテーブルの EMBEDDING 型フィールドで使用され、システムはそのフィールドに関連付けられた埋め込みモデルを自動的に使用するため、この関数で埋め込みモデルの名前を指定する必要はありません。最も類似する結果のみを選択するには、TOP 節を使用します。この例は、入力されたユーザ・クエリに最も似ている 5 つの記述を選択する SQL 文を示しています。(この例では、検索語の埋め込みのプレースホルダとして ? が使用されています。この値は通常、リテラルとしてではなく、パラメータとして指定されるためです。)
SELECT TOP 5 Description FROM Embedding.Example
ORDER BY VECTOR_DOT_PRODUCT(DescriptionEmbedding,
EMBEDDING(?)) DESC
以下の例は、ダイナミック SQL でこのクエリを実行し、結果セットを反復処理して、複数の記述を 1 つの長い文字列に追加する ObjectScript メソッドを示しています。
ClassMethod GetSimilarDesc(searchTerm As %String)
{
set sc = $$$OK
try {
set query = "SELECT TOP 5 Description FROM Embedding.Example
_"ORDER BY VECTOR_DOT_PRODUCT(DescriptionEmbedding,
_"EMBEDDING(?,'my-openai-config')) DESC"
set tStatement = ##class(%SQL.Statement).%New()
$$$ThrowOnError(tStatement.%Prepare(query))
set rset = tStatement.%Execute(searchTerm)
if (rset.%SQLCODE < 0) {
throw ##class(%Exeception.SQL).CreateFromSQLCODE(rset.%SQLCODE,rset.%Message)
}
// process retrieved descriptions here and return the result
set retrievedInfo = ""
while rset.%Next() {
set retrievedInfo = retrievedInfo_" Description: "_rset.%Get("Description")
}
return retrievedInfo
}
catch e {
set sc = e.AsStatus()
return sc
}
}