4
5

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 Database RU23.7で機能追加されたDB内マルチモーダルEmbeddingを試してみた

Posted at

2025年1月にリリースされたRU23.7において、ONNXフォーマットを使ったDB内Embeddingで画像を扱えるようになりました。
一方で、画像のEmbeddingに対応したモデルを23ai向けにONNXエクスポートするには、OML4Py 2.1以上が必要となっています。
そのOML4Py 2.1はRU23.7がリリースされた時点で未だ公開されていなかったのですが、やっと先日使えるようになりました。
という訳で早速、画像のDB内Embeddingを試すべく、せっかくなのでマルチモーダルEmbeddingモデルの clip-vit-large-patch14 を使って動作確認してみました。

なお、今回は OCI BaseDB の RU23.7 を使いました。
またマルチモーダル検索の動作確認にはAPEXで作った簡易アプリを利用しています。
以下のようにテキストで画像を検索するように実装しました。

WS000004.JPG

また画像で画像を検索する機能も試してみました。

WS000005.JPG

本検証用アプリはこちらからエクスポートしたファイルを取得できます。

検証用の画像は kaggle にて CC0: Public Domain で公開されている以下データセットを使用しました。
https://www.kaggle.com/datasets/pavansanagapati/images-dataset

以降は検証手順のメモです。

検証手順

OML4Py 2.1のインストール

OML4Pyに必要なOSパッケージをインストール

$ sudo su -
## 不足パッケージの確認
$ rpm -qa perl-Env
$ rpm -qa libffi-devel
$ rpm -qa openssl 
$ rpm -qa openssl-devel
$ rpm -qa tk-devel
$ rpm -qa xz-devel
$ rpm -qa zlib-devel
$ rpm -qa bzip2-devel
$ rpm -qa readline-devel
$ rpm -qa libuuid-devel
$ rpm -qa ncurses-devel

## 不足分をインストール
$ yum install perl-Env libffi-devel tk-devel bzip2-devel readline-devel libuuid-devel ncurses-devel

Python仮想環境の構築にあたりminicondaをインストール

## Anacondaインストール
$ mkdir -p ~/miniconda3
$ wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O ~/miniconda3/miniconda.sh
$ bash ~/miniconda3/miniconda.sh -b -u -p ~/miniconda3

## 不要ファイル削除
$ rm -rf ~/miniconda3/miniconda.sh

## Anaconda初期セットアップ
$ ~/miniconda3/bin/conda init bash

Python仮想環境を作成

$ conda create -n mltebd python=3.12
## 仮想環境のアクティベート
$ conda activate mltebd

pipを最新化

$ python3 -m pip install --upgrade pip

OML4Pyに必要なPythonパッケージをインストール

$ pip install pandas
$ pip install setuptools
$ pip install scipy
$ pip install matplotlib
$ pip install oracledb
$ pip install joblib
$ pip install scikit-learn
$ pip install numpy
$ pip install onnxruntime
$ pip install onnxruntime-extensions
$ pip install onnx
$ pip install --extra-index-url "https://download.pytorch.org/whl/cpu" torch
$ pip install transformers
$ pip install sentencepiece

以下サイトからOML4Py 2.1 以上をダウンロードし、サーバに配置
https://www.oracle.com/database/technologies/oml4py-downloads.html

zipファイルを解凍し、Perlスクリプトを使ってOML4Pyをインストール

$ mkdir oml4py
$ cd oml4py
$ unzip V1048628-01.zip
$ perl -Iclient client/client.pl

Oracle Machine Learning for Python 2.1 Client.

Copyright (c) 2018, 2025 Oracle and/or its affiliates. All rights reserved.
Checking platform .................. Pass
Checking Python .................... Pass
Checking dependencies .............. Pass
Checking OML4P version ............. Pass
Current configuration
  Python Version ................... 3.12.9
  PYTHONHOME ....................... /home/oracle/miniconda3/envs/mltebd
  Existing OML4P module version .... None

  Operation ........................ Install/Upgrade

Proceed? [yes]

Processing ./client/oml-2.1-cp312-cp312-linux_x86_64.whl
Installing collected packages: oml
Successfully installed oml-2.1

Done

ONNXのエクスポート

以下マニュアルに沿って作業を進めます。
https://docs.oracle.com/en/database/oracle/oracle-database/23/vecse/onnx-pipeline-models-multi-modal-embedding.html

  1. Exporting a pre-configured image model to a file

clip-vit-large-patch14 のONNXファイル作成 (テキスト向けと画像向けの2種類を生成)

from oml.utils import ONNXPipeline
pipeline = ONNXPipeline("openai/clip-vit-large-patch14")
pipeline.export2file("clip")

これで clip_txt.onnxclip_img.onnx の2つが作成されます。

検証用DBユーザ作成、権限付与

GRANT DB_DEVELOPER_ROLE TO dmuser identified by your_password;
GRANT CREATE MINING MODEL TO dmuser;
ALTER USER dmuser QUOTA UNLIMITED ON USERS;

ONNX用のディレクトリオブジェクト作成

CREATE OR REPLACE DIRECTORY dm_dump AS '/u01/app/mltebd';
GRANT READ ON DIRECTORY dm_dump TO dmuser;
GRANT WRITE ON DIRECTORY dm_dump TO dmuser;

ONNXをインポート

clip_txt.onnxclip_img.onnx をDBにインポート

EXECUTE DBMS_VECTOR.LOAD_ONNX_MODEL( 'dm_dump', 'clip_txt.onnx', 'clip_txt');
EXECUTE DBMS_VECTOR.LOAD_ONNX_MODEL( 'dm_dump', 'clip_img.onnx', 'clip_img');

インポートされたことの確認

col model_name format a20
col algorithm_name format a20
col algorithm format a20
col attribute_name format a20
col attribute_type format a20
col data_type format a20 

SELECT model_name, attribute_name, attribute_type, data_type, vector_info
FROM user_mining_model_attributes
WHERE model_name IN ('CLIP_TXT', 'CLIP_IMG')
ORDER BY ATTRIBUTE_NAME;

MODEL_NAME	     ATTRIBUTE_NAME	  ATTRIBUTE_TYPE       DATA_TYPE	    VECTOR_INFO
-------------------- -------------------- -------------------- -------------------- --------------------------------------------------------
CLIP_TXT	     DATA		  TEXT		       VARCHAR2
CLIP_IMG	     DATA		  UNSTRUCTURED	       BLOB
CLIP_IMG	     ORA$ONNXTARGET	  VECTOR	       VECTOR		    VECTOR(768,FLOAT32)
CLIP_TXT	     ORA$ONNXTARGET	  VECTOR	       VECTOR		    VECTOR(768,FLOAT32)

データセット用のディレクトリオブジェクト作成

CREATE OR REPLACE DIRECTORY dataset_dir AS '/u01/app/dataset';
GRANT READ ON DIRECTORY dataset_dir TO dmuser;
GRANT WRITE ON DIRECTORY dataset_dir TO dmuser;

データセットを入れるための表を作成-

CREATE TABLE my_table (
    ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER  NOCYCLE  NOKEEP  NOSCALE  NOT NULL ENABLE,
    file_name VARCHAR2(255),
    mime VARCHAR2(100),
    image BLOB,
    embedding VECTOR
);

表に各データセットの ファイル名、画像バイナリ、画像のベクトルデータ を格納

画像をベクトル化しつつ、表にデータを入れるPythonスクリプトを作成して実行
(なぜかjpgしかベクトル化されなかった)

import os
import oracledb
from oracledb import DB_TYPE_BLOB

# Oracle DB接続情報
user = 'dmuser'
password = 'your_password'
dsn = 'localhost:1521/pdb1'

# BaseDBはNNEによる暗号化を使っているため、
# thick modeで利用しないとDB接続できない
oracledb.init_oracle_client()

connection = oracledb.connect(user=user, password=password, dsn=dsn)

# ディレクトリ内のファイルを取得
directory_path = '/u01/app/dataset'
files = os.listdir(directory_path)

# 各ファイルの内容を読み取り、DBにINSERT
for file_name in files:
    file_path = os.path.join(directory_path, file_name)
    with open(file_path, 'rb') as file:
        content = file.read()
        cr = connection.cursor()
        
        # 画像の読み込み
        blob = cr.var(DB_TYPE_BLOB)
        blob.setvalue(0, content)
        
        # 画像をベクトル化しつつINSERT
        insert_query = "INSERT INTO my_table (file_name, mime, image, embedding) VALUES (:1, 'image/jpeg', :2, VECTOR_EMBEDDING(CLIP_IMG USING TO_BLOB(:2) as DATA))"
        cr.execute(insert_query, [file_name, blob])
        cr.close()

# コミットして接続を閉じる
connection.commit()
connection.close()

APEX、ORDSのインストール

APEXの最新版を取得

$ curl -OL https://download.oracle.com/otn_software/apex/apex-latest.zip
$ unzip apex-latest.zip 

APEXの静的ファイルの格納先を用意

$ mkdir i
$ cp -r -p apex/images i/24.2.0

PDBにログインしてインストール

$ cd apex
$ export NLS_LANG=American_America.AL32UTF8
$ sqlplus / as sysdba
SQL> alter session set container=pdb1;
SQL> @apexins SYSAUX SYSAUX TEMP /i/24.2.0/

APEXの日本語対応

SQL> @load_trans JAPANESE

APEX管理ユーザのパスワード変更

SQL> @apxchpwd

APEX管理ユーザのアンロック

SQL> ALTER USER APEX_PUBLIC_USER NO AUTHENTICATION ACCOUNT UNLOCK;

ORDSインストールのため、JDK 21をインストール

$ curl -OL https://download.oracle.com/java/21/latest/jdk-21_linux-x64_bin.rpm
$ dnf -y localinstall jdk-21_linux-x64_bin.rpm
$ java -version
java version "21.0.7" 2025-04-15 LTS
Java(TM) SE Runtime Environment (build 21.0.7+8-LTS-245)
Java HotSpot(TM) 64-Bit Server VM (build 21.0.7+8-LTS-245, mixed mode, sharing)

ORDSインストール

dnf -y --repofrompath ol8_oracle_software,http://yum.oracle.com/repo/OracleLinux/OL8/oracle/software/x86_64 install ords

ORDSの構成

$ su - oracle
$ export PATH=/usr/local/bin:$PATH
$ cd /etc/ords/config
$ ords install

Oracle REST Data Services - Interactive Install

  Enter a number to select the TNS net service name to use from /u01/app/oracle/product/23.0.0/dbhome_1/network/admin/tnsnames.ora or specify the database connection
    [1] ORCL_QZZ_KIX SERVICE_NAME=orcl_qzz_kix.pubsub1.tstosaka.oraclevcn.com    
    [2] PDB1         SERVICE_NAME=pdb1                                           
    [S] Specify the database connection
  Choose [1]: 2

Connecting to administrator user: SYS AS SYSDBA for container: PDB1 using bequeath connection
  Provide database user name with administrator privileges.
    Enter the administrator username: sys
  Enter the database password for SYS AS SYSDBA: 

Retrieving information.
ORDS is not installed in the database. ORDS installation is required.

  Enter a number to update the value or select option A to Accept and Continue
    [1] Connection Type: TNS
    [2] TNS Connection: TNS_NAME=PDB1 TNS_FOLDER=/u01/app/oracle/product/23.0.0/dbhome_1/network/admin
           Administrator User: SYS AS SYSDBA
    [3] Database password for ORDS runtime user (ORDS_PUBLIC_USER): <generate>
    [4] ORDS runtime user and schema tablespaces:  Default: SYSAUX Temporary TEMP
    [5] Additional Feature: Database Actions
    [6] Configure and start ORDS in Standalone Mode: Yes
    [7]    Protocol: HTTP
    [8]       HTTP Port: 8080
    [9]   APEX static resources location: 
    [A] Accept and Continue - Create configuration and Install ORDS in the database
    [Q] Quit - Do not proceed. No changes
  Choose [A]: 9
  Enter the APEX static resources location: /u01/app/apex/i
  Enter a number to update the value or select option A to Accept and Continue
    [1] Connection Type: TNS
    [2] TNS Connection: TNS_NAME=PDB1 TNS_FOLDER=/u01/app/oracle/product/23.0.0/dbhome_1/network/admin
           Administrator User: SYS AS SYSDBA
    [3] Database password for ORDS runtime user (ORDS_PUBLIC_USER): <generate>
    [4] ORDS runtime user and schema tablespaces:  Default: SYSAUX Temporary TEMP
    [5] Additional Feature: Database Actions
    [6] Configure and start ORDS in Standalone Mode: Yes
    [7]    Protocol: HTTP
    [8]       HTTP Port: 8080
    [9]   APEX static resources location: /u01/app/apex/i
    [A] Accept and Continue - Create configuration and Install ORDS in the database
    [Q] Quit - Do not proceed. No changes
  Choose [A]: 
...()...
2025-04-21T15:17:55.801Z INFO        Oracle REST Data Services initialized
Oracle REST Data Services version : 25.1.0.r1001652
Oracle REST Data Services server info: jetty/12.0.13
Oracle REST Data Services java info: Java HotSpot(TM) 64-Bit Server VM  (build: 21.0.7+8-LTS-245 mixed mode, sharing)

ORDSの自動起動を有効化

$ sudo su -
$ systemctl enable ords
$ systemctl stop ords
$ systemctl start ords

APEX管理画面へアクセスできるよう、BaseDBのポートを開放

$ sudo iptables -I INPUT 8 -p tcp -m state --state NEW -m tcp --dport 8080 -j ACCEPT -m comment --comment "Required for APEX"

$ sudo /sbin/service iptables save
iptables: Saving firewall rules to /etc/sysconfig/iptables: [  OK  ]

以下よりAPEX管理画面へアクセスできることを確認
http://your_public_ip:8080/apex/apex_admin

表内の画像をテキストでベクトル検索するAPEXアプリを作成

画面作成は省略しますが、テキスト入力のフォームとボタンを用意します。
検索結果の表示には対話モード・レポートを使いました。

WS000006.JPG

対話モード・レポートのSQL問い合わせに以下SQLを入力するだけで、テキストから画像を検索するマルチモーダル検索が実装できちゃいます。

SELECT "ROWID","ID","FILE_NAME","MIME",SYS.DBMS_LOB.GETLENGTH("IMAGE")"IMAGE","EMBEDDING" FROM "MY_TABLE"
ORDER BY VECTOR_DISTANCE(embedding, 
    TO_VECTOR(VECTOR_EMBEDDING(CLIP_TXT USING :P5_SEARCH as DATA)), COSINE)
FETCH EXACT FIRST 3 ROWS ONLY

色々と試してみます。
cat で検索
WS000000.JPG

dog で検索
WS000003.JPG

tree で検索
WS000001.JPG

water で検索
WS000002.JPG

表内の画像を画像でベクトル検索するAPEXアプリを作成

ベクトル検索結果を一時格納する表を作成

CREATE TABLE res_table (
    ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER  NOCYCLE  NOKEEP  NOSCALE  NOT NULL ENABLE,
    file_name VARCHAR2(255),
    mime VARCHAR2(100),
    image BLOB
);

検索に使う画像をアップロードするアイテムを追加し、
検索結果の表示用に対話モード・レポートを使用します。

WS000007.JPG

対話モード・レポートのSQL問合せに以下を入力

SELECT "ROWID","ID","FILE_NAME","MIME",SYS.DBMS_LOB.GETLENGTH("IMAGE")"IMAGE" FROM "RES_TABLE" ORDER BY "ID"

以下ベクトル検索処理をAPEXのプロセスとして定義し、
検索ボタンを押すと実行されるように設定します。

DECLARE
    l_file apex_application_temp_files%rowtype;
BEGIN
    -- 一時結果格納表をリセット
    DELETE FROM res_table;
    
    -- 検索用にアップロードされた画像の情報を抽出
    SELECT * INTO l_file
        FROM APEX_APPLICATION_TEMP_FILES
        WHERE name IN 
        (
            SELECT column_value
            FROM APEX_STRING.SPLIT(:P6_SEARCH, ':')
        );
    
    -- 一時結果格納表へデータ保存
    INSERT INTO res_table (file_name, mime, image)
        SELECT file_name, mime, image 
            FROM my_table
            ORDER BY VECTOR_DISTANCE(embedding,
                TO_VECTOR(VECTOR_EMBEDDING(CLIP_IMG USING l_file.BLOB_CONTENT as DATA)), COSINE)
        FETCH EXACT FIRST 3 ROWS ONLY;
    
    COMMIT;
END;

動作確認します。
犬の画像で検索
WS000008.JPG

馬の画像で検索
WS000009.JPG

以上、RU23.7 + OML4Py 2.1 でマルチモーダル検索を実装できました。
テキストからの画像検索はSQL一行で実装できてしまい、とても簡単です。
一方で現段階はインポート可能なONNXファイルのサイズ上限が1GBと厳しく、多言語対応のマルチモーダルEmbeddingモデルを扱うのが難しい仕様となっています。
検索ワードに日本語を使う上で、この点については今後の改善が待たれます。

4
5
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
4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?