Oracle 23aiを使ったAI Vector Searchについて(その２)

約1年前に検証した内容を記載していますので、現時点では変更されている箇所があると思いますので、その点は読み替えていただければと思います。

[前回](https://qiita.com/kousasak/items/e78938f1647b9e372ad2 "Oracle 23aiを使ったAI Vector Searchについて(その１)")から続きまして、実際にパワーポイントやPDF等のファイルからテキストを抽出してベクトル検索を行っていきます。

## １．データ準備
今回は以下の３つのファイルを準備しました。(ダウンロード可能なファイルで準備しています)
a.環境省：地方公共団体のグリーン購入実施のための取組支援事業
b.環境省：「地方公共団体実行計画策定・管理支援システム」構想
c.内閣官房情報通信技術(IT)総合戦略室：官民データ活用推進基本法関連の検討状況について

前回作成したdoc_infoテーブルに以下の情報を登録しています。(上記ファイルのファイル名です)
~~~
ID         DOCNAME
---------- ----------------------------------------------------------------------------------------------------
         1 03_ghoutorikumishien.pptx
         2 900449623.pptx
         3 170223_rikatsu2.pptx
~~~


## ２．テキスト抽出
ここではパワーポイントの中からテキストを抽出する作業を行っていきます。
基本的にはインターネットを探していただければパワーポイントからテキストを抽出するスクリプトは山のように見つかると思うので、気に入ったものを利用して頂ければよいと思います。
今回は普通にテキストを抽出するpythonスクリプトで一気に処理しようと思ってたのですが、いくつか試した結果普通にechoした結果を取り込む形にしました。
※今回はベクトル検索のテストのため簡易的な方法を選択しています。読取精度等は考慮していない為、ファイル検索を行うなど運用を考える場合はきちんと読取精度を考慮した方式を選択してください。
~~~
#!/bin/bash

tempfile='tempcont.sql'
tempfile2='tempcont2.sql'
p_files=(`ls -1 ./powerpoint/`)
f_id=1
OLDIFS=${IFS}
IFS=$'\n'

ret=`sqlplus -s vtest/*****@「接続するDB」 @${tempfile}`

for i in `echo ${ret} | sed -r "s/^\s\s+//" | sed -r "s/\s\s+/\n/g" | sed -r "s/\s/,/g"`;do
        filename=(`echo $i | awk -F, '{print $2}' | awk -F. '{print $1}'`)
        fileid=(`echo $i | awk -F, '{print $1}'`)
        #echo "fileid = ${fileid}"
        for j in `ls -1 text/${filename}*`;do
                pagefile=(`echo $j | awk -F\/ '{print $2}'`)
                pagenum=(`echo ${pagefile} | awk -F. '{print $1}' | awk -F_ '{print $NF}'`)
                content=(`cat ${j} | tr -d '\n' | tr -d "'" | tr -d ' '`)
                echo "insert into contents(id,doc_num,page_num,texts) values(${f_id},${fileid},${pagenum},'${content}');" >> ${tempfile2}
                f_id=$((f_id+1))
        done
done
echo "commit;" >> ${tempfile2}
echo "exit" >> ${tempfile2}

IFS=${OLDIFS}
#sqlplus -s vtest/*****@「接続するDB」 @${tempfile2}
~~~
※最初はDBへのinsertまで自動で行う想定でしたが、確認しながら入れたかったためコメントアウトしています。

ファイル一覧を取ってくるSQL
~~~tempcont.sql
set heading off
set lines 200
set pages 0
col id format 999
col docname for a100
select id,docname from doc_info;
exit
~~~

だいたいDBに入れた感じとしては以下のようになります。
~~~
ID         DOC_NUM    PAGE_NUM   TEXTS
---------- ---------- ---------- --------------------------------------------------------------------------------
         2          1          2 支援事業の背景●組織的に取り組むには・・・グリーン購入調達方針や環境配慮契約
                                 方針を策定することが有効●ところが・・・地方公共団体の方針策定率グリーン購入
                                 調達方針・・・29.6％環境配慮契約方針･････10.0％方針策定時の課題人員不足参考情
                                 報の不足調達コスト増加の懸念方針策定のために必要な支援方針策定のための情報提
                                 供（マニュアル、手順書、仕様書例、他団体の事例等）出典：地方公共団体のグリー
                                 ン購入に関するアンケート調査（令和３年度：環境省）●導入・実施を促すために・
                                 ・・実務支援者*を派遣方針策定や見直し等の実務を支援平成26年度から実施*グリー
                                 ン購入法及び環境配慮契約法に関する知識または経験を有する者

~~~

## ３．テキストのベクトル化
ここでcontentsテーブルにテキストが格納されたので、これらのテキストをベクトル化して格納していきます。
とは言っても以下のような単純なSQLを格納したテキスト分実行するだけの単純な作業になります。
~~~
update contents set v = (select vector_get(texts) from contents where id = 1) where id = 1;
~~~
そして格納されたベクトル化されたものが以下のようになります。
~~~
V
----------------------------------------------------------------------------------------------------------------------------------
[4.39071655E-003,2.684021E-002,-4.8789978E-003,1.42745972E-002,1.20391846E-002,2.54821777E-002,8.32366943E-003,-5.27648926E-002,1.34754181
E-003,8.73565674E-003,-5.6854248E-002,2.74047852E-002,2.07061768E-002,2.36206055E-002,1.2557
9834E-002,2.76489258E-002,1.24053955E-002,-1.96075439E-002,3.68652344E-002,-1.0635376E-002,-
2.50091553E-002,4.3762207E-002,6.5246582E-002,-5.95474243E-003,2.43225098E-002,-3.85437012E-
002,-1.10626221E-002,4.6295166E-002,-3.58581543E-002,-3.58581543E-002,-3.29589844E-002,2.728
27148E-002,-9.18579102E-003,5.01098633E-002,6.7024231E-003,-1.21057034E-004,5.26123047E-002,
-3.52859497E-003,3.07617188E-002,-2.83203125E-002,1.47476196E-002,2.91748047E-002,-9.0484619
1E-003,3.47900391E-002,-1.92260742E-002,1.76696777E-002,1.12533569E-002,2.4520874E-002,-1.13
220215E-002,3.30505371E-002,4.56237793E-002,1.6283989E-004,-7.65228271E-003,2.44598389E-002,
-1.30996704E-002,3.85131836E-002,2.63519287E-002,4.6081543E-002,-9.24110413E-004,2.6184082E-
002,1.23214722E-002,-9.30023193E-003,-5.1612854E-003,1.56402588E-002,-2.92015076E-003,-4.100
79956E-003,-1.26266479E-002,7.23266602E-002,1.89208984E-002,3.71704102E-002,1.91650391E-002,
3.52172852E-002,1.5914917E-002,1.91650391E-002,-1.70135498E-003,1.78985596E-002,1.69067383E-
002,3.38745117E-002,3.89709473E-002,2.44903564E-002,-2.34222412E-002,-7.49588013E-003,-1.227
56958E-002,4.4708252E-003,2.08435059E-002,-1.4793396E-002,-2.13012695E-002,-1.2960434E-003,1
.73091888E-003,-4.6295166E-002,6.2286377E-002,5.37109375E-003,-2.91137695E-002,3.08837891E-0
02,-2.34680176E-002,-2.22930908E-002,-2.07824707E-002,-1.68151855E-002,5.73348999E-003,-2.38
494873E-002,-1.44577026E-002,-4.44641113E-002,-3.0960083E-002,2.02178955E-002,1.06124878E-00
2,3.97949219E-002,2.03399658E-002,1.63726807E-002,4.96520996E-002,9.52911377E-003,4.1015625E
-002,-2.48908997E-003,-2.72369385E-003,-1.05667114E-002,6.85501099E-003,1.84936523E-002,1.70
74585E-002,-7.80105591E-004,1.79443359E-002,-2.96783447E-003,2.57263184E-002,3.11737061E-002
,-3.45802307E-003,-8.5413456E-005,1.34658813E-003,-1.64642334E-002,4.33959961E-002,-1.866149
9E-002,3.93676758E-002,1.46636963E-002,-4.38842773E-002,5.03234863E-002,3.7651062E-003,-3.50
761414E-003,3.06549072E-002,3.53393555E-002,1.83410645E-002,1.04446411E-002,3.66210938E-002,
3.52783203E-002,3.13720703E-002,1.86004639E-002,8.04901123E-003,3.7689209E-002,3.78112793E-0
02,-1.12075806E-002,3.27453613E-002,5.90515137E-002,1.43280029E-002,-1.87835693E-002,4.00390
625E-002,4.44335938E-002,2.07977295E-002,-7.15942383E-002,8.68988037E-003,2.26287842E-002,6.

~~~

## ４．ベクトル検索
ここでようやく本題のベクトル検索に入っていきます。
実際にDB上でテキストとベクトルがcontentsテーブルに格納されているので、こちらにベクトル検索を行っていきます。
以下のようなSQLでベクトル検索を行います。
~~~
SQL> select b.docname,a.page_num,a.texts from contents a,doc_info b where b.id = a.doc_num order by vector_distance(v, vector_get('全国の地方公共団体に接続された情報システムを構築'), EUCLIDEAN) fetch first 3 rows only;
~~~
以下が結果となります。
~~~
DOCNAME                                                 PAGE_NUM   TEXTS
------------------------------------------------------- ---------- --------------------------------------------------------------------------------
900449623.pptx                                                   4 ○　将来的には、全国の地方公共団体に接続された情報システムを構築し、地方公共団
                                                                   体実行計画の策定・実施にかかるPDCAの効率化を促進すると同時に高度化を狙い、地球
                                                                   温暖化対策の推進を加速したい。データベース地方公共団体国（環境省）LGWAN約3,400
                                                                   団体情報の集約：　→地方公共団体の施設情報等を集約・蓄積情報の可視化：　→分析
                                                                   しやすい形に情報を加工し必要な情報を可視化取組の見える化オペレーションの効率化
                                                                   「地方公共団体実行計画策定・管理支援システム」（仮）の概要・イメージ4
170223_rikatsu2.pptx                                            20 【総論①】　公開ルールの徹底【総論②】　民間ニーズを反映する仕組みの整備【総論
                                                                   ③】　オープンデータ・バイ・デザインの推進【総論④】　地方公共団体におけるオー
                                                                   プンデータの取組支援【各論】　個別分野における取組について今後のオープンデータ
                                                                   の推進の方向性19
03_ghoutorikumishien.pptx                                       1  地方公共団体のグリーン購入実施のための取組支援事業

~~~

今回は結果を3行に絞って出力していますが、以上のように、関連性のある文章のテキストが抽出されてきてると思います。
(この例ではLike検索でも可能な感じなのであまりベクトル検索の恩恵が見にくいと思いますが、そこは各環境で色々お試しいただければと思います)