人工知能ベクトルとセマンティック検索
Oracle Database 23aiでは、人工知能(AI)ベクトル検索を使用したセマンティック検索機能が導入されています。これらの機能には、新しいベクトル・データ型、ベクトル索引およびベクトル検索SQL演算子が含まれます。これらを使用すると、データベースはセマンティック・ドキュメント・コンテンツ、イメージおよびその他の非構造化データをベクトルとして格納し、高速でパフォーマンスの高い類似性問合せを実行できます。重要なイノベーションは、データベースが完全一致のみを検索するのではなく、ユーザー・インテントと検索コンテキストをよく理解して類似の一致を検索することです。
この機能に基づいて、Oracleデータベースは、検索拡張生成(RAG)などの生成AI構造を使用して、大規模言語モデル(LLM)とプライベート・ビジネス・データを組み合せて自然言語の質問に応答できます。RAGは、より高い精度を提供し、LLMトレーニング・データへのプライベート・データの公開を回避します。
Oracle Databaseのベクトル・データ型
Oracleデータベースのベクトル型としては、密とスパースという2つがあります。密ベクトルは、Oracle Database 23aiで導入された、最初のAIベクトル型でした。これらは、ほぼすべてのディメンションにゼロ以外の値が格納されているデータセット(つまり、それらでは、非常に豊富で複雑な情報、パターンおよび関係が保持されている)に最適です。密ベクトルの優れたユースケースは、単語の意味の微妙な差異によってさらに適切な一致が識別されるようになる場合のセマンティック検索です。
一方、スパース・ベクトルには、通常は、多数のディメンションがあり、ゼロ以外のディメンション値はほとんどありません。効率を高めるために、それらには、ゼロ以外の値のみが格納されます。これにより、スパース・ベクトルの格納およびメモリー効率が高まります。また、データ・プロファイルにゼロ以外のディメンション値がほとんどないと仮定すると、ODP.NETでは、密ベクトルよりも迅速にそれらを処理できます。これらは、検索フレーズによって関連のある一致ドキュメントを識別するテキスト検索システムなど、その他のAIアプリケーション・タイプに適しています。
ODP.NETのベクトル
バージョン23.3.2以降、管理対象ODP.NETおよびODP.NET Coreでは、既存の.NETまたはODP.NETデータ型(array
、string
、CLOB
およびVARCHAR2
)として、Oracleデータベース・ベクトル・データ・アクセスがサポートされています。管理対象ODP.NETおよびODP.NET Core 23.8では、より自然かつ簡単にベクトルを操作できる、より充実したユーザー・インタフェースのために、密ベクトル(OracleVector
)とスパース・ベクトル(OracleSparseVector
)のための新しいODP.NETベクトル固有型が導入されています。
ODP.NETベクトル・データ型は柔軟に使用できます。1つ以上の次元を持ち、最大は65,536 (64K)次元です。開発者は、固定数または可変数の次元を持つベクトルを定義できます。ベクトル内の各次元の数値形式は、次元ごとに同じ数値形式または異なる数値形式にすることができます:
表3-28 密ベクトルの数値書式
ベクトル数値書式 | 説明 | マップ済.NETタイプ | マップ済ODP.NETタイプ | Size | 例 |
---|---|---|---|---|---|
|
パックUINT8のバイト。各ディメンションは1つのビットです |
|
|
1バイト |
|
|
8ビットの整数 |
|
|
2バイト |
|
|
32ビット浮動小数点 |
|
|
4バイト |
|
|
64ビット浮動小数点 |
|
|
8バイト |
|
たとえば、1つの次元のINT8ベクトルはVector(1, INT8)になります。4つの次元のFLOAT32ベクトルは、Vector(4, FLOAT32)になります。可変次元のFLOAT64ベクトルは、Vector(*, FLOAT64)になります。
各ベクトルの次元がINT8、FLOAT32またはFLOAT64データ型のいずれかになるように指定するには、Vector(4, *)などの*表記法を使用して4次元ベクトルを表します。ベクトルをベクトル(*, *)として指定すると、可変数の次元および可変データ型のベクトルを持つことができます。
表3-29 スパース・ベクトルの数値書式
ベクトル数値書式 | 説明 | マップ済.NETタイプ | マップ済ODP.NETタイプ | Size | 例 |
---|---|---|---|---|---|
|
8ビットの整数 |
|
|
2バイト |
|
|
32ビットの整数 |
|
|
4バイト |
|
|
64ビットの整数 |
|
|
8バイト |
|
ODP.NETのOracleVector
(密)では、4つすべての数値書式を使用できます。ODP.NETのOracleSparseVector
(スパース)では、BINARY
以外のすべてを使用できます。
ODP.NETでは、.NETで次のデータベースのベクトル・データ型を取得できます。
データ型 | ODP.NETまたは.NETデータ型 | 説明 |
---|---|---|
.NET数値配列 |
.NET |
この配列型は、ベクトルの数値形式に応じて |
.NETバイト配列 |
.NET |
Oracle密ベクトルのみ、.NETバイト配列として取得できます。Oracle ノート:
|
.NET文字列 |
.NET |
これには、JSON形式のベクトル表現が含まれています。 |
|
ODP.NET |
これには、JSON形式のベクトル表現が含まれています。 |
|
ODP.NET |
これには、JSON形式のベクトル表現が含まれています。 |
|
ODP.NET |
ODP.NETのネイティブの密ベクトル・データ型。これは、すべてのODP.NETネイティブ・ベクトル・データ型のベース型です。 |
|
ODP.NET |
ODP.NETのネイティブのスパース・ベクトル・データ型。 |
ODP.NETでは、SQLまたはストアド・プロシージャの実行のために、データを次の.NET型としてバインドできます。
データ型 | .NETまたはODP.NETデータ型 | バインド・タイプ | 説明 |
---|---|---|---|
|
ODP.NET |
|
ODP.NETのネイティブのベクトル・データ型。ODP.NETにより、このベクトル・データ型が |
|
ODP.NET |
|
ODP.NETのネイティブの密ベクトル・データ型。ODP.NETでは、バインディング型に基づいてVectorデータ型がバインドされます。 |
|
ODP.NET |
|
ODP.NETのネイティブのスパース・ベクトル・データ型。ODP.NETでは、バインディング型に基づいてVectorデータ型がバインドされます。 |
任意の数値配列 |
.NET |
|
この配列型は、任意の数値配列型にすることができます。ODP.NETでは、数値配列型を推論し、ベクトル・データ型をそれに応じて |
任意の数値配列 |
.NET |
|
この配列型は、任意の数値配列型にすることができます。ODP.NETでは、バインディング型に基づいてVectorデータ型がバインドされます。 |
.NETバイト配列 |
.NET |
|
すべてのベクトル数値形式は、.NETバイト配列としてバインドできます。 |
.NET文字列、OracleString |
.NETまたはODP.NET |
|
これには、JSON形式のベクトル表現が含まれています。ODP.NETにより、このベクトル・データ型が |
.NET文字列、OracleString |
.NETまたはODP.NET |
|
これには、JSON形式のベクトル表現が含まれています。ODP.NETでは、バインディング型に基づいてVectorデータ型がバインドされます。 |
OracleClob |
ODP.NET |
|
これには、JSON形式のベクトル表現が含まれています。 |
ノート:
スパース・ベクトルでは、OracleDbType.Vector_Binary
や.NETバイト配列型としてのバインディングはサポートされていません。
OracleDataAdapter ReturnProviderSpecificTypes
をfalse
に設定してベクトルを使用すると、密ベクトルの場合は、.NET数値配列はFill
メソッドがコールされた後にDataTable
またはDataSet
に移入され、スパース・ベクトルの場合は、そのスパース・ベクトルを表す.NET文字列が使用されます。true
にした場合は、OracleVector
またはOracleSparseVector
が使用されます。
数値書式が'*
'である、ReturnProviderSpecificTypes
がfalse
に設定されている密VECTOR列の場合は、OracleDataAdapter
Fill()
の実行時に、そのVECTORの数値書式に基づいて、short[]
、float[]
、double[]
またはbyte[]
(あるいはそれら両方)の組合せが移入されます。他のすべての書式の密VECTOR列の場合は、そのVECTOR列の数値書式に基づいて、short[]
、float[]
、double[]
またはbyte[]
が移入されます。
ベクトルは相互に直接比較できないため、JOINキー、ORDER BYキー、GROUP BYキーまたはその他の関連シナリオとして使用できません。このため、OracleCommandBuilder
の場合、ベクトルの比較は、生成されたコマンドのSQL WHERE句には含まれません。
CLOBまたはVARCHAR2データ型としてのベクトル
管理対象外ODP.NET、およびバージョン23.3.2より前の管理対象ODP.NETおよびODP.NET Coreでは、既存のCLOB
またはVARCHAR2
データ型を使用するベクトルがサポートされます。これらの場合、ベクトルはJSON形式で格納されます。データの取得および操作では、既存のCLOB
、VARCHAR2
または.NET APIが使用されます。
最近の管理対象ODP.NETおよびODP.NET Coreのバージョンでは、ベクトルは後方互換性の目的で、OracleConfiguration
MapVectorColumnAsClob
プロパティをtrueに設定することで、CLOB
またはVARCHAR2
として格納できます。
スパース・ベクトルが、ネイティブでスパース・ベクトルがサポートされていないODP.NETバージョンに送信されると、そのベクトル・データが、JSON密ベクトル形式でのCLOB
データになります。
ODP.NETのVectorのコード例
//This ODP.NET artificial intelligence (AI) vector sample demonstrates how to insert,
retrieve, update, and delete multi-dimensional FLOAT64, FLOAT32, and INT8 vector data
types using the Oracle database.
//Requires ODP.NET 23ai (23.3.2) or higher and Oracle Database 23ai (23.4) or higher.
//Add User Id, Password, and Data Source, such as Easy Connect Plus or TNS, to the connection
string to connect to the DB.
using Oracle.ManagedDataAccess.Client;
namespace VectorDemo
{
public class VectorDemo
{
//Provide User Id, Password, and Data Source values for your database.
public const string conStr = "User Id=<USER>;Password=<PASSWORD>;Data Source=<DATA SOURCE>;";
public const int id = 1;
public static void Main(string[] args)
{
using (OracleConnection con = new OracleConnection(conStr))
{
using (OracleCommand cmd = con.CreateCommand())
{
try
{
con.Open();
cmd.CommandText = "begin " +
"execute immediate 'drop table VectorTable';" +
"exception when others then if sqlcode <> -942 then raise;" +
"end if;" +
"end;";
cmd.ExecuteNonQuery();
//Create table with 2 dimensional FLOAT64, 3 dimensional FLOAT32, and 4 dimensional INT8 vector columns
cmd.CommandText = "create table VectorTable (id number, float64s vector(2, float64),
float32s vector(3, float32), int8s vector(4, INT8), constraint pk primary key (id))";
cmd.ExecuteNonQuery();
//Insert vector row into DB
InsertVectors();
//Update vector values in DB
UpdateVectors();
//Delete vector values in DB
DeleteVectors();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
public static void InsertVectors()
{
string sql = "insert into VectorTable values (:id, :float64, :float32, :int8)";
try
{
double[] doubles = new double[] { 1.234, 2.345 };
float[] floats = new float[] { 1.23f, 2.34f, 3.45f };
short[] int16s = new short[] { 1, 2, 3, 4 };
OracleConnection con = new OracleConnection(conStr);
con.Open();
OracleCommand cmd = new OracleCommand(sql, con);
cmd.Parameters.Add("id", OracleDbType.Int16, 0, id, System.Data.ParameterDirection.Input);
//Binding FLOAT64 variable as vector - doubles
cmd.Parameters.Add("float64", OracleDbType.Vector, 0, doubles, System.Data.ParameterDirection.Input);
//Binding FLOAT32 variable as vector - floats
cmd.Parameters.Add("float32", OracleDbType.Vector, 0, floats, System.Data.ParameterDirection.Input);
//Binding INT8 variable as vector - int16s
cmd.Parameters.Add("int8", OracleDbType.Vector, 0, int16s, System.Data.ParameterDirection.Input);
// Insert vectors into VectorTable
cmd.ExecuteNonQuery();
//Retrieve vector values from DB
RetrieveVectors();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public static void UpdateVectors()
{
string sql = "update VectorTable set float64s=:float64, float32s=:float32, int8s=:int8 where id=:id";
try
{
double[] doubles = new double[] { 9.876, 8.765 };
float[] floats = new float[] { 9.87f, 8.76f, 7.65f };
short[] int16s = new short[] { 9, 8, 7, 6 };
OracleConnection con = new OracleConnection(conStr);
con.Open();
OracleCommand cmd = new OracleCommand(sql, con);
//Binding FLOAT64 variable as vector - doubles
cmd.Parameters.Add("float64", OracleDbType.Vector_Float64, 0, doubles, System.Data.ParameterDirection.Input);
//Binding FLOAT32 variable as vector - floats
cmd.Parameters.Add("float32", OracleDbType.Vector_Float32, 0, floats, System.Data.ParameterDirection.Input);
//Binding INT8 variable as vector - int16s
cmd.Parameters.Add("int8", OracleDbType.Vector_Int8, 0, int16s, System.Data.ParameterDirection.Input);
cmd.Parameters.Add("id", OracleDbType.Int16, 0, id, System.Data.ParameterDirection.Input);
// Update vectors in VectorTable
cmd.ExecuteNonQuery();
Console.WriteLine("Database vector values updated!");
//Retrieve vector values from DB
RetrieveVectors();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public static void DeleteVectors()
{
string sql = "delete from VectorTable where id=:id";
try
{
OracleConnection con = new OracleConnection(conStr);
con.Open();
OracleCommand cmd = new OracleCommand(sql, con);
cmd.Parameters.Add("id", OracleDbType.Int16, 0, id, System.Data.ParameterDirection.Input);
// Delete row with vector values from VectorTable
cmd.ExecuteNonQuery();
Console.WriteLine("Database vector values deleted!");
//Confirm vectors removed
RetrieveVectors();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public static void RetrieveVectors()
{
string sql = "select * from VectorTable";
try
{
OracleConnection con = new OracleConnection(conStr);
con.Open();
OracleCommand cmd = new OracleCommand(sql, con);
OracleDataReader reader = cmd.ExecuteReader();
//Use ODP.NET vector accessors to retrieve data
if (reader.Read())
{
Console.WriteLine("Retrieve FLOAT64S Vector value:");
double[] vecD = reader.GetDoubleArray(1);
PrintDoubles(vecD);
Console.WriteLine("Retrieve FLOAT32S Vector value:");
float[] vecF = reader.GetFloatArray("FLOAT32S");
PrintFloats(vecF);
Console.WriteLine("Retrieve INT8S vector value:");
short[] vecInt8s = reader.GetInt16Array(3);
PrintInt16s(vecInt8s);
Console.WriteLine();
}
//Return no results if no vector row is found
else
Console.WriteLine("No vector row found.");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
static public void PrintDoubles(double[] doubles)
{
Console.Write("{");
for (int i = 0; i < doubles.Length - 1; i++)
Console.Write(doubles[i].ToString() + ", ");
Console.WriteLine(doubles[doubles.Length - 1].ToString() + "}");
}
static public void PrintFloats(float[] floats)
{
Console.Write("{");
for (int i = 0; i < floats.Length - 1; i++)
Console.Write(floats[i].ToString() + ", ");
Console.WriteLine(floats[floats.Length - 1].ToString() + "}");
}
static public void PrintInt16s(Int16[] int16s)
{
Console.Write("{");
for (int i = 0; i < int16s.Length - 1; i++)
Console.Write(int16s[i].ToString() + ", ");
Console.WriteLine(int16s[int16s.Length - 1].ToString() + "}");
}
}
}