0
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?

Oracle AI Databaseでハイブリッド検索 ― 速度と関連性の両立を目指す

0
Last updated at Posted at 2026-10-05

はじめに

全文検索とベクトル検索を組み合わせるハイブリッド検索。Oracle AI Databaseでは、標準機能を組み合わせて実装できます。

ONNXモデルをOracle AI Databaseに取り込めば、埋め込みやリランクの推論もDB内で実行できます。
外部の推論APIを呼ぶ必要がないため、APIの従量課金やDBと推論サービス間の通信時間を避けられます。ただし、推論にはDBの計算資源を使用します。検索と推論をDB内で完結できるこの構成は、大量の商品を対象に高速な応答が求められるユースケース(ex. 商品検索等)で有効です。

今回はAutonomous AI Database(以下、ADB)を検証環境に使いました。商品検索を例に、その仕組みと検索結果の関連性を確認し、データ件数を増やしたときのベクトル索引による高速化も検証しました。

image.png

ハイブリッド検索の全体フロー

ハイブリッド検索は、次の3つの処理を組み合わせます。

  • 全文検索で、名称や仕様などの言葉に合う候補を取得する。
  • ベクトル検索で、質問と意味が近い候補を取得する。
  • 両方の順位をRRFで統合する。

本稿では、この後にリランクモデルによる再評価も追加し、その効果を検証します。

ADBの機能との対応

これらの検索・モデル推論は、すべてOracle AI Databaseの機能で実行できます。 今回はAutonomous AI Database上で次の機能を使いました。

使用する機能 今回の役割
Oracle Text 日本語のテキストを索引化し、名称や仕様などの言葉に合う商品を全文検索する
ONNXモデル・VECTOR_EMBEDDING DB内に取り込んだintfloat/multilingual-e5-smallで、商品説明や質問の意味を384次元ベクトルに変換する
AI Vector Search・HNSW索引 ベクトルの近さから意味が似た商品を探し、HNSW索引で検索を高速化する
ハイブリッド・ベクトル索引・DBMS_HYBRID_VECTOR.SEARCH 全文検索とベクトル検索を組み合わせ、両方の候補を取得する
UNION+RRF 両方の候補を結合し、それぞれの検索順位を基に統合順位を付ける
SQLの結合 検索結果を業務テーブルと結び付け、価格や在庫情報を取得する
ONNXモデル・PREDICTION DB内に取り込んだBAAI/bge-reranker-v2-m3のINT8版で質問と候補の関連性を再評価し、リランク用のスコアを求める

Oracle TextはDBに組み込まれた全文検索機能です。概要はOracle Text開発者ガイドで確認できます。

DB内の処理フロー

DB内の検索・推論の流れです。RRFで候補を絞り、その候補をリランクで再評価します。

検索とモデル推論の処理を示す図です。埋め込み・リランクともに外部の推論APIは呼びません。

共通の前処理

複数カラムを、検索用の文章にまとめる

今回は、商品名・型番・カテゴリ・説明・用途を連結し、search_textという文字列カラムに保存しました。例えばコピー用紙なら、次の情報を1本の文章にします。

元の情報 例
型番 OF-01-0001
商品名 コピー用紙 A4 500枚 白色度90% オフィスリーフ 01
カテゴリ 用紙・印刷用品
説明・用途 日常の大量印刷に使うコピー用紙。A4、500枚、白色度90%。
型番 + 商品名 + カテゴリ + 説明 + 用途
              ↓
     検索対象の文章(search_text)

全文検索はこの文章をトークンに分けて索引化し、ベクトル検索は同じ文章を埋め込みモデルで数値に変換します。

検索用テーブルに格納する

以下は、連結から格納までをSQLで表した例です。office_productsには商品、office_categoriesにはカテゴリが登録済みとします。

CREATE TABLE office_search_docs (
    product_id NUMBER(10) PRIMARY KEY,
    search_text VARCHAR2(4000 BYTE) NOT NULL
);

INSERT INTO office_search_docs (product_id, search_text)
SELECT p.product_id,
       'passage: ' || p.sku || ' ' || p.product_name || ' ' ||
       c.category_name || ' ' || p.description || ' ' || p.applications
FROM office_products p
JOIN office_categories c ON c.category_id = p.category_id;
COMMIT;

実際のデモでは、連結済みの文章を商品テーブルにも保存し、検索用テーブルへコピーしています。上の例はその前処理を一つにまとめたものです。今回使うE5モデルに合わせ、商品側にはpassage: 、質問側にはquery: を付けます。

全文検索の仕組みと構成

日本語を検索できる形にする

Oracle Textは文章をトークン化し、そのトークンを含む文書を索引から探します。トークンの切り出し方を決めるのがレクサーです。

レクサー・設定 処理の考え方 利点と考慮点
JAPANESE_LEXER 内部辞書で単語を識別。未知語はVGRAMで処理 単語としての一致を扱いやすい。部分文字列で探す用途や固有名詞の分割結果は確認が必要
JAPANESE_VGRAM_LEXER、bigram無効 VGRAM方式で日本語をトークン化 単語境界に限定しない検索に使える。内部ワイルドカード検索を伴う場合がある
JAPANESE_VGRAM_LEXER、bigram有効(今回) 連続する2文字を単位とする2-gramトークンを生成 bigram無効時に比べ、内部ワイルドカード検索を避けられる一方、索引容量が増える

今回は、固有の商品名や仕様表記を文字列として広く拾うため、JAPANESE_VGRAM_LEXERのbigramモードを選びました。速度上の利点はVGRAMのbigram無効時との比較です。JAPANESE_LEXERより高速という意味ではなく、同レクサーにも単語単位の処理による検索精度・応答速度・索引サイズ上の利点があります。Oracle公式:JAPANESE_VGRAM_LEXER / JAPANESE_LEXER

BEGIN
  CTX_DDL.CREATE_PREFERENCE('OFFICE_JA_LEXER', 'JAPANESE_VGRAM_LEXER');
  CTX_DDL.SET_ATTRIBUTE('OFFICE_JA_LEXER', 'bigram', 'true');
END;
/

ここではレクサーの設定を作ります。全文索引そのものは、後述するハイブリッド索引の作成時に構築します。

質問文を検索式へ変換する

検索時は、質問をDBMS_HYBRID_VECTOR.SEARCHのtext.search_textへ渡します。DB側で全文検索式を生成するため、アプリで日本語を分割する処理は不要です。Oracle公式:SEARCH

ベクトル検索の仕組みと構成

埋め込みモデルをDBに取り込む

採用したのは、multilingual-e5-smallのONNXモデルです。日本語を含むテキストを384次元のベクトルに変換します。今回のファイルは、Oracle公式の配布・導入記事で案内されている、文字列の前処理を含む版を使いました。

DBMS_VECTOR.LOAD_ONNX_MODELでOFFICE_E5_SMALLという名前で登録すると、SQLから呼び出せます。他モデルの一覧は付録にまとめました。

SELECT VECTOR_EMBEDDING(
         OFFICE_E5_SMALL USING 'query: 書類を整理したい' AS data
       ) AS embedding
FROM dual;

商品は事前にベクトル化し、検索時は質問だけをベクトル化します。 同じモデルで作ったベクトル同士を比べることで、言葉が違っても意味の近い候補を探せます。

DB内ONNXでは外部推論APIの通信・従量課金を避けられますが、DBの計算資源は使います。OCI Generative AIなどの埋め込みAPIを使う構成も選べます。その場合も商品と質問でモデルを揃えます。Oracle公式:対応プロバイダー

商品のベクトル化と索引を設定する

埋め込みモデルと、近いベクトルを探すHNSW索引を指定します。HNSWは全件の距離計算を省く近似検索で、速さと取りこぼしの両方を後半で検証します。

BEGIN
  DBMS_VECTOR_CHAIN.CREATE_PREFERENCE(
    'OFFICE_VECTORIZER', DBMS_VECTOR_CHAIN.VECTORIZER,
    JSON('{
      "model": "OFFICE_E5_SMALL",
      "vector_idxtype": "HNSW",
      "by": "characters", "max": 2000,
      "overlap": 0, "split": "none", "language": "japanese"
    }')
  );
END;
/

今回は短い商品説明を対象にしています。長文を扱う場合は、モデルの入力上限に合わせたチャンク分割が必要です。

ハイブリッド検索による結果の統合

一つの索引から、両方の検索を実行する

全文検索とベクトル検索の設定を、一つのハイブリッド索引に指定します。

CREATE HYBRID VECTOR INDEX office_hybrid_idx
ON office_search_docs(search_text)
PARAMETERS (
  'VECTORIZER OFFICE_VECTORIZER LEXER OFFICE_JA_LEXER
   MAINTENANCE MANUAL SYNC (MANUAL) OPTIMIZE (MANUAL)'
);

この例は手動同期のため、検索対象データを追加・更新した場合は索引の同期が必要です。

以下はAPIの簡略例です。候補の統合にUNION、順位付けにRRFを明示しています。実測ではE5用のquery: を付けた質問からベクトルを生成し、全文検索用の質問文とは分けて渡しています。

SELECT DBMS_HYBRID_VECTOR.SEARCH(
         JSON('{"hybrid_index_name":"OFFICE_HYBRID_IDX",
                "search_text":"書類を整理したい",
                "search_fusion":"UNION",
                "search_scorer":"RRF"}')
       ) AS search_result
FROM dual;

実装では質問の埋め込みも検索と同じSQL内で生成し、その結果をDBのRESULT_CACHEで再利用します。Oracle公式:SEARCH

RRFで二つの順位をまとめる

RRF(Reciprocal Rank Fusion)は、複数の検索結果を順位で統合する手法です。今回の概念式は次のとおりです。

統合への寄与 = 1 / (60 + 全文検索の順位)
             + 1 / (60 + ベクトル検索の順位)

60は順位差の影響を調整する定数で、今回は両検索のrank_penaltyを60に設定しています。

一方の候補にない商品は、その側の加点がありません。例えば全文だけで1位の商品より、全文で3位・ベクトルで2位の商品が上位になることがあります。異なる尺度の全文スコアとベクトルスコアを直接足さず、両方の検索で上位に来る候補を評価します。表示用の統合スコアは正解確率ではありません。

UNIONで両検索の候補を集め、RRFで順位を付けます。その後、検索対象行のROWIDを使って商品・価格・在庫をSQLで結合します。Oracle公式:DBMS_HYBRID_VECTOR

リランクで候補を再評価する

リランクでは、質問と候補の文章を一緒にモデルへ入力し、関連性を評価し直します。今回採用したのは、多言語対応のBAAI/bge-reranker-v2-m3のINT8版です。

全文 上位100候補 + ベクトル 上位100候補
                  ↓ UNION+RRF
             重複を除いた最大200候補
                  ↓
  OFF:RRF上位20件を表示
   ON:RRF上位30件をDBで再評価 → スコア順の上位20件を表示

候補取得の条件はON/OFF共通です。リランクは、最初の検索で候補に入らなかった商品を追加する処理ではありません。

今回のBGE配布ONNXはトークンID入力だったため、文字列をトークンに変換する処理を前段に追加してからDBへ登録しました。埋め込みモデルは配布済みの文字列対応版を利用し、リランクモデルには加工が必要だった、という違いがあります。

登録後は、PREDICTIONで質問と各候補を評価できます。30候補は1回のSQLで送りますが、推論は候補ごとに実行されます。

SELECT j.product_id,
       PREDICTION(OFFICE_BGE_RERANKER
         USING :query AS DATA1, j.document AS DATA2) AS rerank_score
FROM JSON_TABLE(:documents, '$[*]' COLUMNS (
  product_id NUMBER PATH '$.id',
  document VARCHAR2(4000) PATH '$.text'
)) j;

このモデルの出力は関連性スコアで、確率ではありません。DBMS_HYBRID_VECTOR.SEARCHのsearch_fusion="RERANK"とは別に、RRF後の候補へCross-Encoderを適用しています。Oracle公式:ONNX Reranking Pipeline

検証用アプリケーション

PythonからADBへ接続し、検索結果と応答時間を検証しました。PythonはMac、ADBはus-ashburn-1で動作します。

リランクを使う場合は、検索SQLとリランクSQLを順にADBへ送り、返されたスコアで並べ替えて上位20件を取得します。前掲のDB内の処理フローは、この2回の呼び出しをまとめたもので、単一SQLで完結する実装ではありません。

リランクの有無を切り替えて比較します。質問に含まれる色や仕様を判定して商品を除外する独自ルールは加えていません。以下の測定結果は、繰り返し実行した値を集計しています。

検証結果

何を測ったか

ハイブリッド検索時は、全文・ベクトルから各最大100件を取得し、RRFで統合します。リランクありでは上位30件を再評価し、最終的に20件を取得します。関連性の評価には、そのうち上位10件を使います。

検証は3つに分けました。

検証 データ 確認すること
① 全文検索とベクトル検索の比較 オフィス用品1,000件 質問の種類による得意不得意
② リランクON/OFFの比較 同じ1,000件・8質問 上位結果の改善と追加時間
③ ベクトル検索単体の性能比較 最大100万件の比較専用ベクトル HNSWの速度と厳密検索との一致率

DBはOracle AI Database 26ai(23.26.3.3.0)、索引比較はlowサービス・NO_PARALLEL指定です。現在のADB設定は2 ECPU・自動スケーリングOFFです。主な測定条件は次のとおりです。

項目 条件
埋め込みモデル multilingual-e5-small、384次元
リランクモデル・対象 bge-reranker-v2-m3 INT8、RRF上位30件を再評価
リランク比較 8質問×ON/OFF各3回。Python側で計測し、DBとの通信を含む
ベクトル索引比較 10質問×各10回、各1回ウォームアップ。DB内のPL/SQLで計測し、通信・埋め込み・リランクは含まない
索引・検索条件 HNSW、NEIGHBORS=32、EFCONSTRUCTION=200、COSINE、上位20件
評価方法 商品への適合はPrecision@10、索引の一致率は厳密検索とのRecall@20。時間は中央値

① 全文検索とベクトル検索の比較

文房具、用紙、収納、清掃用品、PC周辺機器など8カテゴリの架空商品1,000件を用意しました。同じ質問で全文検索とベクトル検索をそれぞれ実行し、質問による得意不得意を確認します。リランクは使っていません。

表の「適合条件」を満たす商品が、各方式の上位10件に何件含まれるかを比較します。 最大は10件で、検索の総ヒット数ではありません。適合条件は検索後の評価に使い、検索結果の絞り込みには使っていません。

検索する言葉 適合条件 全文検索 ベクトル検索
ボールペン 青 0.5mm 青・0.5mmのボールペン 9件 9件
USB-C 65W 65WのUSB-C充電器 10件 3件
騒がしい場所でも相手に声を届けたい ヘッドセット 0件 10件
机に散らばるコードを隠したい ケーブルボックス 9件 2件
手を洗った後に水気を取りたい ペーパータオル 0件 4件

ボールペンは、各方式とも青・0.5mmが9件、黒・0.5mmが1件だったため、適合9件と数えています。「USB-C 65W」は65WのUSB-C充電器を探す意図として評価し、ケーブルは含めていません。

商品名や仕様に使われている言葉で探す場面では、全文検索が有効でした。 「USB-C 65W」では、上位10件すべてが65W充電器でした。一方、ベクトル検索では意味の近いUSB-Cケーブルも上位に入り、適合した充電器は3件でした。

商品名を知らず、用途や困りごとから探す場面では、ベクトル検索が有効でした。 「騒がしい場所でも相手に声を届けたい」では、上位10件中の適合商品は全文検索で0件、ベクトル検索で10件でした。商品の説明にある「オンライン会議で周囲の雑音を拾いにくくする」という用途を、別の言い方から探せています。

ただし、質問の言い方だけで得意な方式が決まるわけではありません。「机に散らばるコードを隠したい」では全文検索の方が多く適合しており、商品説明にどのような言葉が含まれるかによっても結果は変わります。この5例は、そうした方式の違いを見るために選んだものです。

ハイブリッド検索の利点は、言葉の一致と意味の近さの両方から候補を集められることです。 今回は全文検索・ベクトル検索から各最大100件を取得し、UNIONで候補をまとめ、RRFでそれぞれの順位を統合スコアに変換して最終順位を付けます。片方の検索だけでは上位に出にくい商品も候補に含められるようにするのが、この仕組みです。

② リランクON/OFFの比較

次に、事前に質問と判定条件を固定した8質問でON/OFFを各3回比較しました。種類と指定仕様が一致した商品を「適合」とし、上位10件に占める割合(Precision@10)を測っています。これは評価用の判定で、検索の絞り込みには使っていません。

8質問の結果 リランクOFF リランクON 変化
上位10件の適合率・平均 95% 100% +5ポイント
Python検索関数の応答時間・中央値 746ms 3,778ms 中央値の差:約3.03秒(約5.1倍)

ここではハイブリッド検索(UNION+RRF)の結果にリランクを追加する場合と、追加しない場合を比較します。具体的に改善したのは「USB-C 65W」です。65W充電器を探す意図として評価すると、次の違いがありました。

「USB-C 65W」の結果 リランクOFF リランクON
上位10件に含まれる適合商品 6件 10件
応答時間・中央値(各3回) 746ms 3,183ms

RRF上位30件を再評価すると、60Wケーブルが下位へ移り、上位10件が65W充電器になりました。この質問では応答時間の中央値が約2.44秒増え、適合商品が4件増えています。①と同じく、充電器を探す意図として評価した結果です。

適合件数が増えたのは8質問中1質問で、残り7質問は同じでした。 今回のモデル・上位30件を再評価する実装では、全体の平均適合率は5ポイント改善した一方、応答時間の中央値は約3.03秒増えました。劇的な改善というほどではなく、この待ち時間をかける価値があるかは用途次第です。

時間は通信を含むPython検索関数の応答時間です。ON/OFFの中央値の差であり、モデルの推論時間だけを測った値ではありません。

③ ベクトル検索単体の性能比較

商品検索とは別のテーブルに、最大100万件の合成商品文章を用意し、全件を同じE5モデル(multilingual-e5-small)でベクトル化しました。件数比較には、同じ生成データを先頭から各件数分使用しました。類似商品の多いテンプレート生成データであり、実商品100万種類の検証ではありません。

ここで測るのは、用意済みの質問ベクトルから上位20件を探す時間です。全文検索・質問の埋め込み・リランクは含みません。

速さと併せて、厳密検索の上位20件をHNSWでも何件取得できたかをRecall@20で測ります。例えば18件一致なら90%です。これはベクトル検索同士の一致率で、前節の商品への適合率とは異なります。

DB内の検索時間を100万件まで比較する

厳密検索の上位20件と、10質問すべてで一致した設定を選んで比較しました。1,000〜1万件はTARGET ACCURACY=95、10万件はefSearch=500、100万件はefSearch=5000です。これは今回確認できた設定であり、あらゆるデータで最適な値という意味ではありません。

件数 全件距離計算・中央値 HNSW・中央値 HNSWの検索設定 Recall@20(全10質問)
1,000 0.689ms 0.180ms TARGET ACCURACY 95 100%
5,000 3.269ms 0.229ms TARGET ACCURACY 95 100%
10,000 8.802ms 0.225ms TARGET ACCURACY 95 100%
100,000 100.899ms 0.337ms efSearch 500 100%
1,000,000 595.818ms 6.189ms efSearch 5000 100%

表の1,000件では時間差が約0.51msですが、100万件では全件距離計算が約596ms、HNSWが約6.19msになりました。索引は1,000件でも高速化に寄与していますが、件数が多いほど短縮できる時間が大きくなっています。

100万件で探索量を変えた結果も比較します。

efSearch DB内の検索時間・中央値 Recall@20・平均 質問別Recallの最小値
2000 3.341ms 99% 90%
5000 6.189ms 100% 100%

efSearchは検索時の探索量です。100万件では、以前の設定2000で平均一致率99%だったため、5000へ増やして再測定しました。今回は全10質問で一致しましたが、未知の質問でも100%になる保証ではありません。実データでは質問を増やして確認する必要があります。

SELECT /*+ NO_PARALLEL */ id,
       VECTOR_DISTANCE(embedding, :v, COSINE) AS distance
FROM office_bench_vectors
ORDER BY VECTOR_DISTANCE(embedding, :v, COSINE)
FETCH APPROXIMATE FIRST 20 ROWS ONLY
WITH TARGET ACCURACY PARAMETERS (efsearch 5000);

時間は冒頭の測定条件に従った計100回の中央値です。100万件の探索量比較には同じ索引を使っています。件数別・設定別の測定は別時点のため、厳密に同時条件の比較ではありません。

Oracle公式:HNSWの探索量設定

まとめ

ONNXモデルの取り込みにより、外部の推論APIを呼ばずに埋め込み・検索・リランクの推論をADB内で実行できました。さらにHNSW索引を組み合わせ、100万件のベクトル検索もミリ秒単位で処理できることを確認しました。

探索量を調整した結果、中央値約3.34ms・厳密検索との平均一致率99%を得られました。さらに探索量を増やすと約6.19msで、検証した10質問すべての上位20件が厳密検索と一致しました。いずれもDB内のベクトル検索単体の時間で、埋め込み・リランク・通信は含みません。

リランクも試しましたが、今回のモデル・条件では改善は限定的でした。平均適合率は95%から100%へ上がったものの、適合件数が増えたのは8質問中1質問で、通信を含む応答時間の中央値は約0.75秒から約3.78秒へ増えています。導入する価値はありますが、常に使うべきとまでは言えません。検索結果の改善に数秒の待ち時間をかけられるか、用途に合わせて選ぶのがよさそうです。

モデル一覧と公式資料は付録にまとめています。

付録

モデル一覧と公式資料

モデルを探すときは、次の公式リンクを使えます(2026年10月5日確認)。

確認したいこと リンク・見方
ONNX変換用の事前設定モデル一覧 Oracle公式:Text Embeddingの変換手順。手順9に show_preconfigured() の出力例がある
Oracleが配布する変換済みONNXパイプライン Oracle公式:AI models in ONNX pipeline format。確認時点では multilingual_e5_base を掲載
DBへの取り込み条件・メタデータ Oracle公式:DBMS_VECTOR.LOAD_ONNX_MODEL。埋め込み・リランクの登録方法を確認できる

事前設定一覧に掲載されているテキスト埋め込みモデルの例です。以下は一覧からの抜粋で、モデル名のリンクは各モデルの説明・利用条件を確認するためのものです。

モデル 位置付け
intfloat/multilingual-e5-small 今回採用した埋め込みモデル
intfloat/multilingual-e5-base 同じ多言語E5系列の選択肢
sentence-transformers/paraphrase-multilingual-mpnet-base-v2 多言語モデルの比較候補
ibm-granite/granite-embedding-278m-multilingual 多言語モデルの比較候補
sentence-transformers/all-MiniLM-L6-v2 公式の変換手順で使用されるモデル
BAAI/bge-small-en-v1.5 英語向けモデルの比較候補

OML4Pyを利用できる環境では、インストールしたバージョンの一覧を次のコードで確認できます。この一覧には画像・分類・リランクなども含まれるため、すべてがテキスト埋め込みモデルではありません。

from oml.utils import ONNXPipelineConfig
ONNXPipelineConfig.show_preconfigured()

「変換用の事前設定があること」と「DBにそのまま登録できるONNXファイルが配布されていること」は別です。 前者はOML4Pyなどで前処理を含むONNXパイプラインに変換してから取り込みます。対応はDB・変換ツールのバージョンにも依存するため、上記を全ADB環境での動作保証一覧としては扱いません。

リランクモデル

Oracle公式の事前設定モデル一覧とONNX Reranking Pipelineを起点に、今回採用したモデルと比較候補を整理しました。ADBでの確認状況は次のとおりです。

モデル・入手先 特徴 公式手順・今回の確認状況
BAAI/bge-reranker-base Cross-Encoder型のリランクモデル Oracle公式にONNX変換・DB登録・SQL推論の例がある
BAAI/bge-reranker-v2-m3 / INT8 ONNX 多言語対応。今回採用 配布ONNXに文字列入力の前処理を追加し、今回のADBで登録・推論を確認済み
Qwen3-Reranker-0.6B 多言語・指示対応。yes/noトークンの確率から関連性を評価 比較候補。今回のADBでは未検証
Jina Reranker v2 base multilingual 多言語対応 比較候補。今回のADBでは未検証

公式手順の掲載と、今回の実機での動作確認は区別しています。この表は取り込み可能な全モデルを網羅するものではありません。

0
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
0
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?