人工知能ベクトルとセマンティック検索

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データ型(arraystringCLOBおよび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

BINARY

パックUINT8のバイト。各ディメンションは1つのビットです

Byte

OracleVector

1バイト

Vector(1024, BINARY)Vector(1024, BINARY, DENSE)

INT8

8ビットの整数

Int16

OracleVector

2バイト

Vector(4, INT8)Vector(4, INT8, DENSE)

FLOAT32

32ビット浮動小数点

Float

OracleVector

4バイト

Vector(768, FLOAT32)Vector(768, FLOAT32, DENSE)

FLOAT64

64ビット浮動小数点

Double

OracleVector

8バイト

Vector(10000, FLOAT64)Vector(10000, FLOAT64, DENSE)

たとえば、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

INT8

8ビットの整数

Int16

OracleSparseVector

2バイト

Vector(4, INT8, SPARSE)

FLOAT32

32ビットの整数

Float

OracleSparseVector

4バイト

Vector(768, FLOAT32, SPARSE)

FLOAT64

64ビットの整数

Double

OracleSparseVector

8バイト

Vector(10000, FLOAT64, SPARSE)

ODP.NETのOracleVector (密)では、4つすべての数値書式を使用できます。ODP.NETのOracleSparseVector (スパース)では、BINARY以外のすべてを使用できます。

ODP.NETでは、.NETで次のデータベースのベクトル・データ型を取得できます。

データ型 ODP.NETまたは.NETデータ型 説明

.NET数値配列

.NET

この配列型は、ベクトルの数値形式に応じてInt16[]float[]またはdouble[]になります。

.NETバイト配列

.NET

Oracle密ベクトルのみ、.NETバイト配列として取得できます。Oracle BINARYの数値形式は、.NETバイト配列として取得する必要があります。

ノート:

InvalidCastExceptionは、Get*Array()メソッド(GetByteArray()メソッドを除く)に対してBINARY形式でスローされます。

.NET文字列

.NET

これには、JSON形式のベクトル表現が含まれています。

OracleString

ODP.NET

これには、JSON形式のベクトル表現が含まれています。

OracleClob

ODP.NET

これには、JSON形式のベクトル表現が含まれています。

OracleVector

ODP.NET

ODP.NETのネイティブの密ベクトル・データ型。これは、すべてのODP.NETネイティブ・ベクトル・データ型のベース型です。

OracleSparseVector

ODP.NET

ODP.NETのネイティブのスパース・ベクトル・データ型。

ODP.NETでは、SQLまたはストアド・プロシージャの実行のために、データを次の.NET型としてバインドできます。

データ型 .NETまたはODP.NETデータ型 バインド・タイプ 説明

OracleVectorOracleSparseVector

ODP.NET

OracleDbType.Vector

ODP.NETのネイティブのベクトル・データ型。ODP.NETにより、このベクトル・データ型がOracleVectorProviderTypeとしてバインドされます。

OracleVector

ODP.NET

OracleDbType.Vector_Binary

OracleDbType.Vector_Int8

OracleDbType.Vector_Float32

OracleDbType.Vector_Float64

ODP.NETのネイティブの密ベクトル・データ型。ODP.NETでは、バインディング型に基づいてVectorデータ型がバインドされます。

OracleSparseVector

ODP.NET

OracleDbType.Vector_Int8

OracleDbType.Vector_Float32

OracleDbType.Vector_Float64

ODP.NETのネイティブのスパース・ベクトル・データ型。ODP.NETでは、バインディング型に基づいてVectorデータ型がバインドされます。

任意の数値配列

.NET

OracleDbType.Vector

この配列型は、任意の数値配列型にすることができます。ODP.NETでは、数値配列型を推論し、ベクトル・データ型をそれに応じてOracleDbType.Vector_Int8OracleDbType.Vector_Float32,またはOracleDbType.Vector_Float64としてバインドします。

任意の数値配列

.NET

OracleDbType.Vector_Int8

OracleDbType.Vector_Float32

OracleDbType.Vector_Float64

この配列型は、任意の数値配列型にすることができます。ODP.NETでは、バインディング型に基づいてVectorデータ型がバインドされます。

.NETバイト配列

.NET

OracleDbType.Vector

OracleDbType.Vector_Binary

OracleDbType.Vector_Int8

OracleDbType.Vector_Float32

OracleDbType.Vector_Float64

すべてのベクトル数値形式は、.NETバイト配列としてバインドできます。BINARYベクトルは、.NETバイト配列としてのみバインドできます。

.NET文字列、OracleString

.NETまたはODP.NET

OracleDbType.Vector

これには、JSON形式のベクトル表現が含まれています。ODP.NETにより、このベクトル・データ型がOracleDbType.Vector_Float32としてバインドされます

.NET文字列、OracleString

.NETまたはODP.NET

OracleDbType.Vector_Int8

OracleDbType.Vector_Float32

OracleDbType.Vector_Float64

これには、JSON形式のベクトル表現が含まれています。ODP.NETでは、バインディング型に基づいてVectorデータ型がバインドされます。

OracleClob

ODP.NET

OracleDbType.Clob

OracleDbType.Varchar2

これには、JSON形式のベクトル表現が含まれています。

ノート:

スパース・ベクトルでは、OracleDbType.Vector_Binaryや.NETバイト配列型としてのバインディングはサポートされていません。

OracleDataAdapter ReturnProviderSpecificTypesfalseに設定してベクトルを使用すると、密ベクトルの場合は、.NET数値配列はFillメソッドがコールされた後にDataTableまたはDataSetに移入され、スパース・ベクトルの場合は、そのスパース・ベクトルを表す.NET文字列が使用されます。trueにした場合は、OracleVectorまたはOracleSparseVectorが使用されます。

数値書式が'*'である、ReturnProviderSpecificTypesfalseに設定されている密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形式で格納されます。データの取得および操作では、既存のCLOBVARCHAR2または.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() + "}");
        }
    }
}