Oracle Database 23aiにHuggin Face上のONNXフォーマットでモデルをインポート/Embeddingしてみた

## はじめに
本記事はOracle 23ai Free版とvectorユーザー（[こちらの記事を参考に20分ほどで終わります](https://qiita.com/msasakaw/items/9ef45294aa569eaeec89)）の作成が完了していることを前提に書いています。



Oracle Database 23aiを触っていると社内の先輩からお声がけいただきました。
__Oracle Database 23aiではONNXフォーマットでエクスポートした学習済みEmbeddingモデルをインポートし、Embeddingに使用することができますよー。__

・・・
・・・・
・・・？？
(-_-)zzz 読み方わからん
## ONNXフォーマットってなんやねん
### 概要
機械学習モデルを表現するために使用されるフォーマット
- Open Neural Network eXchangeの略
- オニキスと読む
- オープンソースのフォーマット

### ONNXができた理由
フレームワークの相互運用性を高めるため
- モデルの運用は各々の環境に特化しており、別の環境に移すと動作しないことが多々あった
- じゃあ学習したフォーマット、１回統一した形式に出力してから使えばいいやん！
といった経緯らしいです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763355/c5098895-f05c-0674-e16d-2c9e23345b02.png)

### ONNX フォーマットにして嬉しい理由
- いろんな言語/ハードウェアで動かせる
    - 言語：C++, C#, Java, Node.js, Ruby, Pythonなど
    - ハードウェア：CPU, Nvidia GPUのほかAMD GPUやNPU、FPGAも対応
-  今後ますますONNXの普及が進んでいく可能性大
    -  Amazon、Microsoft、FaceBookなど有名企業が協業して開発中

なんとなくわかった気がします。
### Oracle Database 23aiにONNXフォーマットでモデルをインポートする理由は？
- データをDB外に動かさずEmbeddingできる
- Genarative AIをAPIコールのためユーザーのクレデンシャルを設定しなくて良い
- APIコール分のネットワークレイテンシーがのらない
- (条件を満たせば)任意の学習モデルをインポートできる

## 1. 事前準備
DBユーザ「vector」でログインします。
```sQL
sqlplus vector/vector@freepdb1
```
ディレクトリ・オブジェクトを作成、必要な権限を付与します。
```SQL
CREATE OR REPLACE DIRECTORY VEC_DUMP AS '/home/oracle';
GRANT READ, WRITE ON DIRECTORY VEC_DUMP TO vector;
COMMIT;
```
## 2. OML4Pyセットアップ
### ONNXフォーマット使用要件
Oracle Databaseに学習済みモデルをONNXフォーマットでimportするには、 __OML4Pyを使ったONNXのexportが必要です。__（OML4Py以外でexportされたONNXフォーマットはImportできない・・・）

というわけで、OML4Pyをインストールしていきます。

### 必要なOSパッケージインストール
rpm -qaコマンドでそれぞれの不足パッケージを全て確認します。
```Shell
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コマンドで不足しているパッケージをインストールしていきます。
```Shell
yum install perl-Env libffi-devel tk-devel bzip2-devel readline-devel libuuid-devel ncurses-devel
```

OML4Pyをインストールするため、Anacondaで仮想環境を管理します。
Anacondaのインストール
```Shell
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
```
PATHを通して、Anaconda初期セットアップは完了です！
```
~/miniconda3/bin/conda init bash
```
ターミナルをもう一度立ち上げると、condaコマンドが使えるようになります！
仮想環境を新規作成します。

`conda create -n (任意の仮想環境名) python=(任意のPythonバージョン)`

実行したコマンド
```Python
conda create -n test python=3.12
```
仮想環境testのアクティベート
```Python
conda activate test
```
```
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
```

Oracle Technology NetworkからOracle Machine Learning for Python をダウンロードページにアクセスし、  [23ai Client]モジュールをローカルにダウンロードします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763355/853f6010-1514-f534-b08e-51e9c81ef95d.png)

ディレクトリ移動
```Shell
mkdir oml4py
cd oml4py
```
scpコマンドなどで↑で作成したディレクトリに先ほどダウンロードしたZIPファイルを移動させます。
```shell
unzip oml4py-client-linux-x86_64-2.0.zip
perl -Iclient client/client.pl
```
Proceed？聞かれるのでYesと答えます。
```
Successfully installed oml-2.0

Done
```
OML4Pyのセットアップが完了しました！

## 3.Huggin Face上のONNXモデルをエクスポートする
[Use Guide](https://docs.oracle.com/en/database/oracle/machine-learning/oml4py/2/mlugp/convert-trained-models-onnx-format.html)を参考に手順を踏んでいきます。
まず、Pythonの対話型シェルを起動します。
```
Python3
```
 [Hugging Face](https://huggingface.co/models?sort=trending)から任意のモデルをダウンロードしてください。
 今回は104カ国語に対応しているマルチリンガルモデル`google-bert/bert-base-multilingual-cased`を使用しました。
 
 注意事項
 - ※Exportモデルのサイズは1GBまで
 - `quantize_model=True`で1GBに調整可能
 
 2 つのクラス`EmbeddingModel,EmbeddingModelConfig`をインポートします。
```Python
from oml.utils import EmbeddingModel, EmbeddingModelConfig
```
テンプレートからモデルの構成オブジェクトを作成します。 
- text：テンプレートの名前
- max_seq_length=512：モデルへの入力の最大長トークン数
```Python
config = EmbeddingModelConfig.from_template("text",max_seq_length=512,quantize_model=True)
```
事前定義されているインスタンスの初期化を行います。
- model_name：ロードする事前トレーニング済みモデルの名前
```Python
em = EmbeddingModel(model_name="google-bert/bert-base-multilingual-cased", config=config)
```
- 第1引数：ユーザー定義の ONNX モデル名
- 第2引数：ONNXファイルが保存される出力ディレクトリを指定
    - "."は現在のディレクトリ


```Python
em.export2file("bert-base-multilingual-cased",output_dir=".")
```
oml4pyディレクトリに`bert-base-multilingual-cased.onnx`というファイルがエクスポートされているはずです！

## 4.ONNXフォーマットをOracle Database 23aiにインポート

事前準備で作成したVEC_DUMPディレクトリに`bert-base-multilingual-cased.onnx`を移動させておきます。
まずは、
DBユーザ「vector」でログインします。
```sQL
sqlplus vector/vector@freepdb1
```
DBMS_VECTOR.LOAD_ONNX_MODELプロシージャを使用して、エクスポートしたONNXファイルをDBにインポートします。
- ディレクトリ名 ： VEC_DUMP
- インポートするONNXファイル名 ： `bert-base-multilingual-cased.onnx`
- 登録する任意のモデル名 ： doc_model
- JSON形式のメタ情報
    - function : embedding (モデルの種類)
    - embeddingOutput : emveddingOutput (生成されたエンベディングを保持するモデル出力)
    - input ： モデルの入力マッピングについて説明

```SQL
EXECUTE DBMS_VECTOR.LOAD_ONNX_MODEL( 'DATA_PUMP_DIR', 'bert-base-multilingual-cased.onnx', 'minilm_model', JSON('{"function" : "embedding", "embeddingOutput" : "embedding", "input": {"input": ["DATA"]}}'));
```

## 5.インポートしたモデルでEmbeddingしてみる
せっかくマルチリンガルモデルなので、”こんにちは”をベクトル化してみます。

```SQL
SELECT TO_VECTOR(VECTOR_EMBEDDING(doc_model USING 'こんにちは' as data)) AS embedding;
```
結果
```SQL
EMBEDDING
--------------------------------------------------------------------------------
[1.83466617E-002,-4.49530967E-002,3.72901559E-002,1.14019075E-002,1.05774663E-00
2,2.98821665E-002,2.26891469E-002,4.69735526E-002,-5.6950625E-002,9.14487895E-00
3,2.65075881E-002,9.30174813E-002,3.87203395E-002,2.77586859E-002,1.06022535E-00
2,-2.85519883E-002,5.75444959E-002,-2.07790714E-002,-3.01839504E-002,5.22442684E
-002,-6.59843311E-002,8.64373427E-003,3.98213342E-002,2.27618758E-002,1.18214637
E-002,7.86895156E-002,-6.24861009E-002,-4.87056151E-002,3.57667147E-003,-3.91889
773E-002,1.36257557E-003,-3.31115089E-002,1.89379032E-003,-4.13463637E-003,8.255
51888E-006,-6.73396746E-003,-1.36754327E-002,5.08497879E-002,5.99995516E-002,-1.
04993302E-002,-1.41968597E-002,1.8587349E-002,-7.74052751E-004,-2.60757166E-003,
-1.66333839E-002,8.1936596E-004,3.29511464E-002,8.40951875E-003,-1.39681762E-002
,5.94577491E-002,6.20501721E-003,8.87873396E-003,-1.3176783E-002,-9.61824786E-00
・
・
・
EMBEDDING
--------------------------------------------------------------------------------
0441185E-003,-1.79465283E-002,5.06018698E-002,1.10141179E-002,3.07282936E-002,-4
.75509139E-003,1.82689645E-003,4.12070677E-002,-4.29137098E-003,1.27261691E-002,
-1.34949297E-001,1.11087831E-002,-1.00812279E-002,-9.97149795E-002,-6.79327995E-
002,6.30223602E-002,6.57042861E-002,
```
長いですが、ベクトル化できていることが確認できました！！
追加でONNXモデルを呼び出してベクトル検索もできたらいい…な…（書きます）

## 参考
大変参考にさせていただきました！
- Oracle Database 23ai FreeとAPEXでRAGを使った生成AIアプリをローコード開発してみた (事前準備編)
https://qiita.com/ssfujita/items/34d9e3c39ca730d29351
- Oracle Machine Learning for Pythonをインストールしてみた。(2024/01/30)
https://qiita.com/kenwatan/items/43d4776456d041718bc5