hotchpotch/japanese-splade-v2のモデルを使ってsparseベクトルを生成し、Oracle Database 23aiに保存して類似検索まで実行してみます。
Hugging Faceのhotchpotch/japanese-splade-v2のモデル・カードは以下です。
作成された方
@hotchpotch (セコン)さんによる解説記事は以下です。私には難しくて、内容は理解できていません。
SPLADE モデルの作り方・日本語SPLADEテクニカルレポート 高性能な日本語SPLADE(スパース検索)モデルを公開しました 情報検索モデルで最高性能(512トークン以下)・日本語版SPLADE v2をリリース
前回の記事 ではBAAI/bge-m3を使ってdenseベクトルとsparseベクトルを生成して、Oracle Databaseに保存するまでを実装しました。今回はjapanese-splade-v2で生成したsparseベクトルを使って、類似検索を行うAPEXアプリケーションを作成してみます。
作業手順はBAAI/bge-m3のときとほぼ同じです。
今回もClaude Sonnet 4に、hotchpotch/japanese-splade-v2のモデルを使ってsparseベクトルを生成するFastAPIのサーバーを作ってもらいました。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from transformers import AutoTokenizer, AutoModelForMaskedLM
import torch
import numpy as np
from typing import Dict, List
import logging
from contextlib import asynccontextmanager
# ロギング設定
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# グローバル変数でモデルとトークナイザーを保持
tokenizer = None
model = None
class TextRequest(BaseModel):
text: str
max_length: int = 512
class SparseVector(BaseModel):
indices: List[int]
values: List[float]
vocab_size: int
class TextResponse(BaseModel):
text: str
sparse_vector: SparseVector
async def load_model():
"""モデルを読み込み"""
global tokenizer, model
try:
logger.info("Japanese SPLADE v2モデルを読み込み中...")
model_name = "hotchpotch/japanese-splade-v2"
# トークナイザーとモデルを読み込み
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForMaskedLM.from_pretrained(model_name)
# GPUが利用可能な場合はGPUを使用
device = "cuda" if torch.cuda.is_available() else "cpu"
model = model.to(device)
model.eval()
logger.info(f"モデルの読み込みが完了しました (device: {device})")
except Exception as e:
logger.error(f"モデル読み込み中にエラーが発生しました: {e}")
raise e
async def cleanup_model():
"""モデルをクリーンアップ"""
global tokenizer, model
logger.info("モデルをクリーンアップ中...")
tokenizer = None
model = None
@asynccontextmanager
async def lifespan(app: FastAPI):
# 起動時
await load_model()
yield
# 終了時
await cleanup_model()
app = FastAPI(
title="Japanese SPLADE API",
description="Japanese SPLADE v2モデルを使用してsparse vectorを生成するAPI",
version="1.0.0",
lifespan=lifespan
)
def splade_max_pooling(logits, attention_mask):
relu_log = torch.log(1 + torch.relu(logits))
weighted_log = relu_log * attention_mask.unsqueeze(-1)
max_val, _ = torch.max(weighted_log, dim=1)
return max_val
def create_sparse_vector(text: str, max_length: int = 512) -> SparseVector:
"""テキストからsparse vectorを生成"""
if tokenizer is None or model is None:
raise HTTPException(status_code=500, detail="モデルが読み込まれていません")
try:
device = next(model.parameters()).device
# テキストをトークン化
tokens = tokenizer(
text,
return_tensors="pt",
truncation=True,
padding=True,
max_length=max_length
)
# デバイスに転送
tokens = {k: v.to(device) for k, v in tokens.items()}
# モデルの推論を実行
with torch.no_grad():
outputs = model(**tokens)
logits = outputs.logits # [batch_size, seq_len, vocab_size]
# splade_max_pooling関数を使用してsparse vectorを生成
embeddings = splade_max_pooling(logits, tokens['attention_mask'])
# バッチサイズが1なので最初の要素を取得
vector = embeddings[0] # [vocab_size]
# 非ゼロの要素のみを取得(閾値を設定してスパース性を確保)
threshold = 1e-6
non_zero_mask = vector > threshold
non_zero_indices = torch.nonzero(non_zero_mask).squeeze(-1)
non_zero_values = vector[non_zero_indices]
# CPUに移動してリストに変換
indices = non_zero_indices.cpu().numpy().tolist()
values = non_zero_values.cpu().numpy().tolist()
return SparseVector(
indices=indices,
values=values,
vocab_size=tokenizer.vocab_size
)
except Exception as e:
logger.error(f"sparse vector生成中にエラーが発生しました: {e}")
raise HTTPException(status_code=500, detail=f"処理中にエラーが発生しました: {str(e)}")
@app.post("/encode", response_model=TextResponse)
async def encode_text(request: TextRequest):
"""テキストをsparse vectorにエンコード"""
try:
sparse_vector = create_sparse_vector(request.text, request.max_length)
return TextResponse(
text=request.text,
sparse_vector=sparse_vector
)
except Exception as e:
logger.error(f"エンコード処理中にエラーが発生しました: {e}")
raise HTTPException(status_code=500, detail=str(e))
@app.post("/batch_encode")
async def batch_encode_texts(texts: List[str], max_length: int = 512):
"""複数のテキストを一度にエンコード"""
try:
results = []
for text in texts:
sparse_vector = create_sparse_vector(text, max_length)
results.append({
"text": text,
"sparse_vector": {
"indices": sparse_vector.indices,
"values": sparse_vector.values,
"vocab_size": sparse_vector.vocab_size
}
})
return {"results": results}
except Exception as e:
logger.error(f"バッチエンコード処理中にエラーが発生しました: {e}")
raise HTTPException(status_code=500, detail=str(e))
@app.get("/health")
async def health_check():
"""ヘルスチェック"""
return {
"status": "healthy",
"model_loaded": tokenizer is not None and model is not None,
"cuda_available": torch.cuda.is_available(),
"device": str(next(model.parameters()).device) if model is not None else "unknown"
}
@app.get("/model_info")
async def get_model_info():
"""モデル情報を取得"""
if tokenizer is None:
raise HTTPException(status_code=500, detail="モデルが読み込まれていません")
return {
"model_name": "hotchpotch/japanese-splade-v2",
"vocab_size": tokenizer.vocab_size,
"max_position_embeddings": getattr(tokenizer, 'model_max_length', 'unknown'),
"device": str(next(model.parameters()).device) if model is not None else "unknown"
}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=7999)
Oracle APEXのアプリケーションからこのAPIサーバーを呼び出して、文字列のsparseベクトルを生成してデータベースに保存します。japanese-SPLADE-v2が生成するsparseベクトルの次元数は32768なので、BAAI/bge-m3とは異なりベクトル型の次元数の上限は超えません。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
create table ebaj_sparse_vectors (
id number generated by default on null as identity
constraint ebaj_sparse_vectors_id_pk primary key,
text varchar2(4000 char),
is_updated boolean default true,
vec vector(32768, float32, sparse)
);
FastAPIサーバーを実行します。作業はmacOSで行います。Python 3.13とpodman(またはDocker)を使います。
必要なスクリプトをhttps://github.com/ujnak/splade-serviceからダウンロードできるようにしています。
リポジトリをクローンし、作成されたディレクトリに移動します。
git clone https://github.com/ujnak/splade-service cd splade-service
% git clone https://github.com/ujnak/splade-service
Cloning into 'splade-service'...
remote: Enumerating objects: 15, done.
remote: Counting objects: 100% (15/15), done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 15 (delta 1), reused 4 (delta 1), pack-reused 0 (from 0)
Receiving objects: 100% (15/15), 5.86 KiB | 5.86 MiB/s, done.
Resolving deltas: 100% (1/1), done.
% cd splade-service
splade-service %
Pythonの仮想環境をspladeとして作成し、アクティベートします。Pythonのバージョンは3.13を指定します。
python3.13 -m venv splade . splade/bin/activate splade-service % python3.13 -m venv splade
splade-service % . splade/bin/activate
(splade) splade-service %
使用するパッケージをインストールします。
pip install -r requirements.txt
(splade) splade-service % pip install -r requirements.txt
Collecting fastapi>=0.104.0 (from -r requirements.txt (line 1))
Using cached fastapi-0.115.12-py3-none-any.whl.metadata (27 kB)
Collecting uvicorn>=0.24.0 (from uvicorn[standard]>=0.24.0->-r requirements.txt (line 2))
[中略]
Using cached hf_xet-1.1.2-cp37-abi3-macosx_11_0_arm64.whl (2.5 MB)
Using cached idna-3.10-py3-none-any.whl (70 kB)
Using cached MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl (12 kB)
Using cached mpmath-1.3.0-py3-none-any.whl (536 kB)
Using cached urllib3-2.4.0-py3-none-any.whl (128 kB)
Using cached sniffio-1.3.1-py3-none-any.whl (10 kB)
Installing collected packages: unidic-lite, mpmath, websockets, uvloop, urllib3, typing-extensions, tqdm, sympy, sniffio, setuptools, safetensors, regex, pyyaml, python-dotenv, protobuf, packaging, numpy, networkx, MarkupSafe, idna, httptools, hf-xet, h11, fugashi, fsspec, filelock, click, charset-normalizer, certifi, annotated-types, uvicorn, typing-inspection, requests, pydantic-core, jinja2, anyio, watchfiles, torch, starlette, pydantic, huggingface-hub, tokenizers, fastapi, transformers
Successfully installed MarkupSafe-3.0.2 annotated-types-0.7.0 anyio-4.9.0 certifi-2025.4.26 charset-normalizer-3.4.2 click-8.2.1 fastapi-0.115.12 filelock-3.18.0 fsspec-2025.5.1 fugashi-1.4.3 h11-0.16.0 hf-xet-1.1.2 httptools-0.6.4 huggingface-hub-0.32.3 idna-3.10 jinja2-3.1.6 mpmath-1.3.0 networkx-3.5 numpy-2.2.6 packaging-25.0 protobuf-6.31.1 pydantic-2.11.5 pydantic-core-2.33.2 python-dotenv-1.1.0 pyyaml-6.0.2 regex-2024.11.6 requests-2.32.3 safetensors-0.5.3 setuptools-80.9.0 sniffio-1.3.1 starlette-0.46.2 sympy-1.14.0 tokenizers-0.21.1 torch-2.7.0 tqdm-4.67.1 transformers-4.52.4 typing-extensions-4.13.2 typing-inspection-0.4.1 unidic-lite-1.0.8 urllib3-2.4.0 uvicorn-0.34.3 uvloop-0.21.0 watchfiles-1.0.5 websockets-15.0.1
[ notice ] A new release of pip is available: 25.0.1 -> 25.1.1
[ notice ] To update, run: pip install --upgrade pip
(splade) splade-service %
以上で準備が完了です。APIサーバーを起動します。
python server.py (splade) splade-service % python server.py
INFO : Started server process [ 73108 ]
INFO : Waiting for application startup.
INFO:__main__:Japanese SPLADE v2モデルを読み込み中...
INFO:__main__:モデルの読み込みが完了しました (device: cpu)
INFO : Application startup complete.
INFO : Uvicorn running on http://0.0.0.0:7999 (Press CTRL+C to quit)
別ターミナルからAPIサーバーのテストを行います。
splade-service 以下にある
curl-01.sh を実行します。
sh curl-01.sh
APIサーバーが返すベクトルが表示されれば、APIサーバーの実行は成功です。
splade-service % sh curl-01.sh
{"text":"これは日本語のテストです","sparse_vector":{"indices":[429,4262,5630,12499,12500,12538,13037,13374,13449,13459,13618,13821,14877,14985,22460,24096],"values":[0.10150858014822006,0.17410790920257568,0.27124056220054626,0.49592137336730957,1.5145821571350098,0.7409901022911072,0.228782057762146,0.06052016094326973,0.5196099281311035,0.4933662414550781,0.023301351815462112,0.5448876023292542,0.43785205483436584,1.2762678861618042,0.10169661045074463,0.08509977906942368],"vocab_size":32768}}
splade-service %
CTRL+Cを入力しAPIサーバーを停止します。
これからコンテナ・イメージを作成し、APIサーバーをpodmanのコンテナとして実行できるようにします。splade-service以下にDockerfileがあるので、それを使ってコンテナ・イメージを作成します。
podman build --file Dockerfile --tag japanese-splade-v2 .
(splade) splade-service % podman build --file Dockerfile --tag japanese-splade-v2 .
STEP 1/5: FROM python:3.13
STEP 2/5: WORKDIR /app
--> 6abc32147ceb
STEP 3/5: COPY server.py requirements.txt .
--> 3b79e4f8fa7d
STEP 4/5: RUN pip install -r requirements.txt
Collecting fastapi>=0.104.0 (from -r requirements.txt (line 1))
Downloading fastapi-0.115.12-py3-none-any.whl.metadata (27 kB)
Collecting uvicorn>=0.24.0 (from uvicorn[standard]>=0.24.0->-r requirements.txt (line 2))
Downloading uvicorn-0.34.3-py3-none-any.whl.metadata (6.5 kB)
Collecting transformers>=4.35.0 (from -r requirements.txt (line 3))
Downloading transformers-4.52.4-py3-none-any.whl.metadata (38 kB)
Collecting torch>=2.0.0 (from -r requirements.txt (line 4))
Downloading torch-2.7.0-cp313-cp313-manylinux_2_28_aarch64.whl.metadata (29 kB)
Collecting numpy>=1.24.0 (from -r requirements.txt (line 5))
Downloading numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.metadata (63 kB)
Collecting pydantic>=2.0.0 (from -r requirements.txt (line 6))
Downloading pydantic-2.11.5-py3-none-any.whl.metadata (67 kB)
Collecting fugashi>=1.3.0 (from -r requirements.txt (line 7))
Downloading fugashi-1.4.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.metadata (7.1 kB)
Collecting protobuf>=4.21.0 (from -r requirements.txt (line 8))
Downloading protobuf-6.31.1-cp39-abi3-manylinux2014_aarch64.whl.metadata (593 bytes)
Collecting unidic-lite>=1.0.8 (from -r requirements.txt (line 9))
Downloading unidic-lite-1.0.8.tar.gz (47.4 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 47.4/47.4 MB 40.8 MB/s eta 0:00:00
Installing build dependencies: started
Installing build dependencies: finished with status 'done'
Getting requirements to build wheel: started
Getting requirements to build wheel: finished with status 'done'
Preparing metadata (pyproject.toml): started
Preparing metadata (pyproject.toml): finished with status 'done'
[中略]
Created wheel for unidic-lite: filename=unidic_lite-1.0.8-py3-none-any.whl size=47658912 sha256=4f876a371019546e995b130a47ebadbe7d21e3494b8d7cc631425ce450eac8d6
Stored in directory: /root/.cache/pip/wheels/93/40/f2/23f8a0da599c4174200d54d0b933aedc464a12f1061e4aabba
Successfully built unidic-lite
Installing collected packages: unidic-lite, mpmath, websockets, uvloop, urllib3, typing-extensions, tqdm, sympy, sniffio, setuptools, safetensors, regex, pyyaml, python-dotenv, protobuf, packaging, numpy, networkx, MarkupSafe, idna, httptools, hf-xet, h11, fugashi, fsspec, filelock, click, charset-normalizer, certifi, annotated-types, uvicorn, typing-inspection, requests, pydantic-core, jinja2, anyio, watchfiles, torch, starlette, pydantic, huggingface-hub, tokenizers, fastapi, transformers
Successfully installed MarkupSafe-3.0.2 annotated-types-0.7.0 anyio-4.9.0 certifi-2025.4.26 charset-normalizer-3.4.2 click-8.2.1 fastapi-0.115.12 filelock-3.18.0 fsspec-2025.5.1 fugashi-1.4.3 h11-0.16.0 hf-xet-1.1.2 httptools-0.6.4 huggingface-hub-0.32.3 idna-3.10 jinja2-3.1.6 mpmath-1.3.0 networkx-3.5 numpy-2.2.6 packaging-25.0 protobuf-6.31.1 pydantic-2.11.5 pydantic-core-2.33.2 python-dotenv-1.1.0 pyyaml-6.0.2 regex-2024.11.6 requests-2.32.3 safetensors-0.5.3 setuptools-80.9.0 sniffio-1.3.1 starlette-0.46.2 sympy-1.14.0 tokenizers-0.21.1 torch-2.7.0 tqdm-4.67.1 transformers-4.52.4 typing-extensions-4.13.2 typing-inspection-0.4.1 unidic-lite-1.0.8 urllib3-2.4.0 uvicorn-0.34.3 uvloop-0.21.0 watchfiles-1.0.5 websockets-15.0.1
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager, possibly rendering your system unusable. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv. Use the --root-user-action option if you know what you are doing and want to suppress this warning.
[notice] A new release of pip is available: 25.0.1 -> 25.1.1
[notice] To update, run: pip install --upgrade pip
--> 7813f267ac01
STEP 5/5: CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "7999", "--log-level", "info", "--access-log"]
COMMIT japanese-splade-v2
--> 6db870d05d4c
Successfully tagged localhost/japanese-splade-v2:latest
6db870d05d4c5b1d14d11376eb7555b46705a226efe0305983b9f5ff9c9c4f26
(splade) splade-service %
イメージjapanese-splade-v2 が作成されたことを確認します。
podman image ls japanese-splade-v2 (splade) splade-service % podman image ls japanese-splade-v2
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/japanese-splade-v2 latest 6db870d05d4c 2 minutes ago 2.36 GB
(splade) splade-service %
作成したコンテナ・イメージからコンテナ
splade を作成し、実行します。
podman run -d --name splade -p 7999:7999 localhost/japanese-splade-v2
(splade) splade-service % podman run -d --name splade -p 7999:7999 localhost/japanese-splade-v2
2fe3d3d0128995768069f125ba52ab1adfad5a6ac44074bcbbe5b898cd73e532
(splade) splade-service %
コンテナのログを確認します。Uvicornが開始しリクエストを待ち受けていれば、sparseベクトルの生成ができる状態です。
(splade) splade-service % podman logs -f splade
INFO: Started server process [1]
INFO: Waiting for application startup.
INFO:server:Japanese SPLADE v2モデルを読み込み中...
INFO:server:モデルの読み込みが完了しました (device: cpu)
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:7999 (Press CTRL+C to quit)
作成したAPIサーバーを呼び出し表EBAJ_SPARSE_VECTORSの列VECを更新する、また、クエリ文書を入力して、表EBAJ_SPRSE_VECTORSの列との距離を計算するアプリケーションを作成します。エクスポートは以下にあります。
https://github.com/ujnak/apexapps/blob/master/exports/sparse-similarity.zip
今回もローカルのマシンのコンテナで実行しているAPEX環境にインポートして、アプリケーションを使用します。環境の作成方法については、記事「
podmanを使ってOracle Database FreeとOracle REST Data Servicesをコンテナとして実行する 」で紹介しています。
アプリケーションをインポートして実行すると、以下のような画面が開きます。
作成 ボタンをクリックすると、ベクトルを生成する文字列を入力するフォームが開きます。
Claude Sonnet 4にSPLADE類似検索テストケースを生成してもらいました。
機械学習におけるTransformerアーキテクチャは、自然言語処理タスクで画期的な性能を示しています。Self-Attentionメカニズムにより、文脈情報を効率的に捉えることができ、BERTやGPTなどの事前学習モデルの基盤となっています。
高類似度(期待順位:1位)
Transformerモデルは自然言語処理分野で革命的な変化をもたらしました。注意機構(Attention Mechanism)を用いることで、長距離依存関係を効果的に学習し、BERTやGPTといった大規模言語モデルの開発を可能にしました。
中類似度(期待順位:2位)
深層学習ニューラルネットワークは画像認識や音声認識など様々な分野で応用されています。CNNやRNNといったアーキテクチャが主流でしたが、近年はTransformerベースのモデルも注目されています。
低類似度(期待順位:3位)
データサイエンスプロジェクトにおいて、適切な前処理とモデル選択が重要です。回帰分析や分類問題に対して、ランダムフォレストやサポートベクターマシンなどの手法が効果的です。
無関係(期待順位:4位)
今日の天気は晴れており、気温は25度です。公園では多くの家族連れが散歩を楽しんでいます。桜の花が美しく咲いており、春の訪れを感じさせます。
糖尿病は血糖値の調節機能に問題がある代謝疾患です。主に1型と2型に分類され、適切な食事療法と運動療法、必要に応じて薬物療法を組み合わせた治療が重要です。
高類似度(期待順位:1位)
糖尿病患者の血糖管理には、食事制限、定期的な運動、インスリン療法などの包括的なアプローチが必要です。1型糖尿病と2型糖尿病では治療方針が異なります。
中類似度(期待順位:2位)
高血圧は生活習慣病の一つで、心血管疾患のリスクを高めます。塩分制限、適度な運動、ストレス管理が予防と治療の基本となります。
低類似度(期待順位:3位)
健康的な生活習慣には、バランスの取れた食事、規則正しい睡眠、適度な運動が重要です。これらは様々な疾病の予防に効果的です。
無関係(期待順位:4位)
新しいスマートフォンの機能には、高解像度カメラ、長時間バッテリー、5G通信対応が含まれています。ユーザーインターフェースも大幅に改善されました。
企業のデジタルトランスフォーメーション(DX)推進には、既存業務プロセスの見直し、ITインフラの整備、従業員のスキル向上が不可欠です。顧客体験の向上と業務効率化を同時に実現することが求められます。
高類似度(期待順位:1位)
デジタル化の進展により、企業は業務プロセスの自動化とシステム統合を進めています。従業員の研修とITインフラ投資により、生産性向上と顧客満足度の改善を目指しています。
中類似度(期待順位:2位)
現代の企業経営では、テクノロジーを活用した競争優位性の確保が重要です。クラウドサービスやAI技術の導入により、新たなビジネスモデルの創出が可能になります。
低類似度(期待順位:3位)
マーケティング戦略の立案には、ターゲット顧客の分析と市場動向の把握が必要です。ブランディングと効果的な広告展開により売上向上を図ります。
無関係(期待順位:4位)
週末の登山計画を立てています。天候を確認し、必要な装備を準備して、安全に山頂を目指したいと思います。自然の美しさを満喫する予定です。
気候変動が生態系に与える影響について、海洋酸性化と気温上昇による生物多様性の減少が深刻な問題となっています。特に珊瑚礁生態系では白化現象が頻発し、海洋生物の生息環境が脅かされています。
高類似度(期待順位:1位)
地球温暖化による海水温の上昇は、珊瑚の白化現象を引き起こし、海洋生態系の破綻を招いています。海洋の酸性化も相まって、海洋生物の多様性維持が困難になっています。
中類似度(期待順位:2位)
環境保護の観点から、持続可能な開発目標の達成が重要です。森林保全、海洋保護、温室効果ガス削減などの取り組みにより、地球環境の保全を図る必要があります。
低類似度(期待順位:3位)
再生可能エネルギーの普及により、太陽光発電や風力発電の技術開発が進んでいます。エネルギー効率の向上と環境負荷の軽減が期待されています。
無関係(期待順位:4位)
料理レシピの開発において、食材の組み合わせと調理法の工夫が重要です。栄養バランスを考慮しながら、美味しい料理を作ることを心がけています。
Precision@k : 上位k件中の関連文書数
Recall@k : 全関連文書中の上位k件での検索率
MRR (Mean Reciprocal Rank) : 最初の関連文書の順位の逆数
NDCG@k : 正規化割引累積利得
各テストケースで、高類似度文書が1位、中類似度が2位、低類似度が3位、無関係が4位にランクされることを期待します。SPLADEのスパース表現による語彙マッチングと意味的類似性の両方を考慮した検索性能を評価できます。
候補文書群をフォームから入力します。
文字列が表EBAJ_SPARSE_VECTORSの列TEXTに保存されます。この後、ボタン
Update Vectors をクリックするとAPIサーバーが呼び出され、列VECが更新されます。
その前に、APIサーバーのエンドポイントをアプリケーションに設定します。
APIサーバーのエンドポイントは、アプリケーション定義の置換文字列
G_ENDPOINT として設定しています。APEXが動作しているコンテナから、コンテナの外のホストに接続するため
host.containers.internal がホスト名になります。
http://host.containers.internal:7999/encode ボタンUpdate Vectors を押したときに、以下のPL/SQLのコードが実行されます。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
declare
l_request json_object_t;
l_request_clob clob;
l_response clob;
l_response_json json_object_t;
e_call_api_failed exception;
l_sparse_vector json_object_t;
l_sarr json_array_t;
l_sarr_clob clob;
begin
apex_web_service.set_request_headers('Content-Type', 'application/json');
for r in (select * from ebaj_sparse_vectors where is_updated)
loop
l_request := json_object_t();
l_request.put('text', r.text);
l_request_clob := l_request.to_clob();
l_response := apex_web_service.make_rest_request(
p_url => :G_ENDPOINT
,p_http_method => 'POST'
,p_body => l_request_clob
);
if apex_web_service.g_status_code <> 200 then
raise e_call_api_failed;
end if;
l_response_json := json_object_t(l_response);
l_sparse_vector := l_response_json.get_object('sparse_vector');
l_sarr := json_array_t();
l_sarr.append(l_sparse_vector.get_number('vocab_size'));
l_sarr.append(l_sparse_vector.get_array('indices'));
l_sarr.append(l_sparse_vector.get_array('values'));
l_sarr_clob := l_sarr.to_clob();
update ebaj_sparse_vectors set vec = to_vector(l_sarr_clob, 32768, float32, sparse), is_updated = false where id = r.id;
end loop;
commit;
end;
列VECが、japanese-splade-v2のモデルを呼び出して生成したsparseベクトルで更新されます。
ナビゲーション・メニューの
Search より、検索ページを開きます。
QueryにSPLADE類似検索テストケースのクエリ文書を入力し、ボタンQuery をクリックします。クエリ文書のsparseベクトルを生成して、表EBAJ_SPARSE_VECTORSの列VECとの距離の昇順に並べてレポートを表示します。
レポートのSQL問い合わせとして、以下を設定しています。
SELECT id, text,
VECTOR_DISTANCE(vec, VECTOR(:P3_QUERY_VECTOR, 32768, FLOAT32, SPARSE), DOT) as distance
FROM ebaj_sparse_vectors
ORDER BY
VECTOR_DISTANCE(vec, VECTOR(:P3_QUERY_VECTOR, 32768, FLOAT32, SPARSE), DOT)
FETCH FIRST 10 ROWS ONLY;
ボタンQueryをクリックしたときに以下のPL/SQLコードを実行しています。
正直なところ、SPLADEモデル自体については理解が及びませんが、データベースにsparseベクトルを保存して検索するAPEXアプリケーションは作れそうです。
今回の記事は以上になります。
Oracle APEXのアプリケーション作成の参考になれば幸いです。
追記
splade-service以下にtoken_values.pyというファイルを置いています。SPLADEの推論・単語トークンの確認を行うコードを含んでいます。yasemをインストールして実行すると、"
これは日本語のテストです "という文書のトークンを確認できます。
pip install yasem
(splade) splade-service % pip install yasem
Collecting yasem
Using cached yasem-0.4.1-py3-none-any.whl.metadata (4.5 kB)
Requirement already satisfied: numpy>=2.0.0 in ./splade/lib/python3.13/site-packages (from yasem) (2.2.6)
Collecting scipy>=1.13.1 (from yasem)
Using cached scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl.metadata (61 kB)
Requirement already satisfied: torch>=2.2.0 in ./splade/lib/python3.13/site-packages (from yasem) (2.7.0)
Requirement already satisfied: transformers>=4.44.0 in ./splade/lib/python3.13/site-packages (from yasem) (4.52.4)
Requirement already satisfied: filelock in ./splade/lib/python3.13/site-packages (from torch>=2.2.0->yasem) (3.18.0)
Requirement already satisfied: typing-extensions>=4.10.0 in ./splade/lib/python3.13/site-packages (from torch>=2.2.0->yasem) (4.13.2)
[中略]
Using cached yasem-0.4.1-py3-none-any.whl (7.3 kB)
Using cached scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl (22.4 MB)
Installing collected packages: scipy, yasem
Successfully installed scipy-1.15.3 yasem-0.4.1
[ notice ] A new release of pip is available: 25.0.1 -> 25.1.1
[ notice ] To update, run: pip install --upgrade pip
(splade) splade-service %
python token_values.py
実行すると、SPLADEの場合、単語そのものに加えて類似した単語にもスコアがつくことが確認できます。
(splade) splade-service % python token_values.py
{'日本': 1.5146484375, 'テスト': 1.2763671875, 'これ': 0.74072265625, '言語': 0.5458984375, '言葉': 0.5205078125, 'この': 0.496337890625, '試験': 0.49267578125, '検査': 0.4375, '語': 0.2705078125, 'です': 0.2301025390625, '私': 0.1734619140625, 'か': 0.1029052734375, 'みたい': 0.10205078125, 'わかり': 0.08514404296875, 'ここ': 0.0596923828125, '種類': 0.0241241455078125}
(splade) splade-service %
完