2024年11月13日水曜日

Hugging Face Transformers.jsのObject detectionをOracle APEXで実装してみる

Hugging FaceのTransformers.jsのExamplesに含まれているIn-browser object detectionを、Oracle APEXのアプリケーションとして実装してみます。この例の名前がVanilla JavaScriptであることから分かるように、APEXに組み込みやすいサンプルになっています。GitHubのリポジトリへのリンクはこちらです。

作成したアプリケーションは以下のように動作します。

タヌキはdog、シマウマはzebraとして認識されました。


空のAPEXアプリケーションを作成し、ホーム・ページに機能を実装しています。

ホーム・ページのページ・プロパティCSSインラインに、以下を記述します。サンプルに含まれているstyle.cssの定義を、ほぼそのまま貼り付けています。



解析する画像を選択するページ・アイテムとしてP1_IMAGEを作成しています。タイプイメージ・アップロードです。


ページ・アイテムP1_STATUSに設定した文字列をステータスとして表示します。タイプ表示のみです。


画像を表示する要素およびオブジェクトを検出するJavaScriptコードは、すべて静的コンテンツのリージョンに記述します。サンプルに含まれているindex.jsのコードを、ほぼそのまま貼り付けています。今回はimportmapの定義を省略しています。


以上でObject detectorのアプリケーションは完成です。

今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/transformers-js-object-detector.zip

Oracle APEXのアプリケーション作成の参考になれば幸いです。

ONNX Runtime WebのQuick StartをOracle APEXに組み込んでみる

ONNX Runtime WebQuick StartをOracle APEXのアプリケーションに組み込んでみます。ONNX Runtime WebはONNX Runtimeによる機械学習モデルをブラウザのJavaScriptで実行するという、Microsoftさんによるプロジェクトです。

ONNX Runtime WebのQuick Startは、画面に一行表示するだけの簡単なスクリプトです。


本記事は、ONNX Runtime WebをOracle APEXに組み込むための作法の紹介になります。

GitHubの以下のページより、Quick Startのコードやモデルを取得します。


参照するコードが記述されたファイルは、ESモジュールを使ったサンプルであるindex_esm.htmlです。model.onnxは、Oracle APEXの静的アプリケーション・ファイルとして保存するため、あらかじめGitHubよりダウンロードしておきます。


空のAPEXアプリケーションを作成し、ホーム・ページにQuick Startを組み込みます。

ONNX Runtime Webを組み込むページのページ・プロパティHTMLヘッダーに、以下のimportmapを定義します。主にWasmを使うことを想定しており、WebGLやWebGPUの設定は含めていません。それらは必要に応じて追記するとよいでしょう。
<script type="importmap">
    {
        "imports": {
            "onnxruntime-web": "https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/esm/ort.min.js",
            "onnxruntime-web/wasm": "https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/esm/ort.wasm.min.js"
        }
    }
</script>

共有コンポーネント静的アプリケーション・ファイルとして、先ほどGitHubのリポジトリよりダウンロードしたmodel.onnxをアップロードします。参照#APP_FILES#model.onnxになります。


静的コンテンツのリージョンを作成し、index_esm.htmlの内容をほぼそのまま転記します。index_esm.html内では、document.bodyに結果を出力していますが、APEXのアプリケーションでは静的コンテンツのリージョンに含まれるDIV要素に出力するように変更しています。



以上でONNX Runtime WebのQuick Startが動作するようになりました。

簡単なアプリケーションですが、アプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/onnx-runtime-web-quick-start.zip

Oracle APEXのアプリケーション作成の参考になれば幸いです。

2024年11月12日火曜日

Oracle APEXのページにTensorFlow.jsを組み込みInception_v3による分類を実行する

O'Reillyから出版されている亀が表紙の本「Learning TensorFlow.js」のChapter 5、Introducing Modelsで紹介されている、Inception_v3による分類をOracle APEXのアプリケーションに組み込んでみました。Chapter 5は、Inception_v3を使った分類とペットの顔を検出する2つのサンプルの実装を含んでいますが、分類の方を実装しています。

TensorFlow.jsをOracle APEXに組み込む、といっても特別な作業ではなく、HTMLのページに実装できるものであれば、大抵はAPEXのアプリケーションに組み込めます。TensorFlow.jsも同様です。

今回作成したAPEXアプリケーションは以下のように動作します。

リスはsquirrel、レッサーパンダはlesser panda、シマウマはzebraと分類されました。


ホーム・ページにすべての機能を実装しています。


TensorFlow.jsの状態を表示するために、ページ・アイテムP1_STATUSを作成しています。タイプ表示のみで、JavaScriptのコード中よりページ・アイテムの値を設定します。


分類の対象となる画像は、静的コンテンツのリージョンに表示します。ソースHTMLコードに以下を記述します。

<img id="mystery" src="" class="w80p" />

属性srcの値は、ページ・アイテムP1_IMAGEの動的アクション中で設定します。


分類の結果はページ・アイテムP1_OBJECTSに出力します。タイプテキスト領域とし、複数行を表示できるようにしています。


分類する画像を選択するページ・アイテムとしてP1_IMAGEを作成します。タイプイメージ・アップロードです。このアプリケーションはサーバーにページを送信することはありません。画像ファイルをブラウザ上で選択するだけで、イメージのアップロードは行いません。


ページ・アイテムP1_IMAGEで画像を選択すると、変更イベントが発生します。変更イベントにより呼び出される動的アクション(のTRUEアクション)として、JavaScriptコードの実行を選び、設定コードに以下を記述します。
// get image file and its URL for img src.
const imageFile = this.triggeringElement.files[0];
const imageUrl = URL.createObjectURL(imageFile);
document.getElementById("mystery").src = imageUrl;
// Reset Status
apex.items.P1_OBJECTS.setValue("");
apex.items.P1_STATUS.setValue("Ready for Detection");
ページ・アイテムに選択された画像を、静的コンテンツに含まれるIMG要素の属性SRCに設定しています。そうすることにより、選択した画像がIMG要素として画面に表示されます。


ページ・ロード時にTensorFlow.jsを初期化します。

ページ・プロパティJavaScriptファイルURLとして、以下を記述します。

https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@4.22.0/dist/tf.min.js

ファンクションおよびグローバル変数の宣言に以下を記述します。
var model;
var INCEPTION_CLASSES = [];

ページ・ロード時に実行に、以下を記述します。TensorFlow.jsの初期化が完了したのち、Inception_v3のモデルを読み込んでいます。また、分類のラベルをテキスト・ファイルからJSON配列INCEPTION_CLASSESに読み込んでいます。
/*
 * ページ・ロード時にTensorFlow.jsが読み込まれる。TensorFlow.jsがReadyになったら、続けてモデルを読み込む。
 * ページを離れたらロードしたモデルは破棄されると思うので、モデルにたいしてはdispose()は呼び出していない。
 */
tf.ready().then(() => {
    // const modelPath = "https://tfhub.dev/google/tfjs-model/imagenet/inception_v3/classification/3/default/1";
    const modelPath = "https://www.kaggle.com/models/google/inception-v3/TfJs/classification/2";
    apex.items.P1_STATUS.setValue("TensorFlow.js is Ready, Loading Model...");
    tf.loadGraphModel(modelPath, { fromTFHub: true }).then((m) => {
        model = m; // modelはページ・グローバルな変数
        apex.items.P1_STATUS.setValue("Model is Loaded");
    });
});

/*
 * ImageNetのラベルを配列INCEPTION_CLASSESに読み込む。
 */
fetch('https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt')
    .then(response => {
        if (!response.ok) {
            throw new Error('Failed to get ImageNetLabels.txt');
        }
        return response.text();
    })
    .then( (text) => {
        // テキストを行ごとに分割し、配列に変換
        INCEPTION_CLASSES = text.split('\n').filter(label => label.trim() !== '');
        console.log(INCEPTION_CLASSES);
    })
    .catch(error => {
        console.error('Failed to read ImageNetLabels.txt:', error);
    });
モデルのロードが完了してから(P1_STATUSModel is Loadedと表示されたのち)、画像の分類を実行できるようになりますが、それまでの間、ボタンを無効化するなどの処理は行っていないため、注意が必要です。


kaggle.comでホストされているモデルのブラウザへの読み込みを許可するため、アプリケーション定義セキュリティブラウザ・セキュリティのセクションにあるHTTPレスポンス・ヘッダーに以下を記述します。

Access-Control-Allow-Origin: https://www.kaggle.com


最後に分類を実行するボタンDETECTを作成し、ボタンをクリックしたときに呼び出される動的アクションを定義します。

実行するJavaScriptのコードは以下になります。
tf.tidy(() => {
    const mysteryImage = document.getElementById("mystery");
    const myTensor = tf.browser.fromPixels(mysteryImage);
    // Inception v3 expects an image resized to 299x299
    const readyfied = tf.image
        .resizeBilinear(myTensor, [299,299], true)
        .div(255)
        .reshape([1,299,299,3]);
    apex.items.P1_STATUS.setValue("Start prediction !");
    // predict.
    const result = model.predict(readyfied);
    result.print();
    apex.items.P1_STATUS.setValue("Prediction Done !");

    const { values, indices } = tf.topk(result, 3);
    indices.print();

    // Let's hear those winners
    const winners = indices.dataSync();
    
    apex.items.P1_OBJECTS.setValue(
`First place ${INCEPTION_CLASSES[winners[0]]},
Second place ${INCEPTION_CLASSES[winners[1]]},
Third place ${INCEPTION_CLASSES[winners[2]]}`);

});

以上でアプリケーションは完成です。実行すると記事の先頭にあるGIF動画のように動作します。

今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/tensorflow-js-classification.zip

Oracle APEXのアプリケーション作成の参考になれば幸いです。

2024年11月11日月曜日

Oracle APEXのアプリのページにPyodideを組み込んでみる

PyodideのGetting startedのページを参照して、Oracle APEXのアプリケーションに簡単なPythonのスクリプトを実行するページを作ってみました。

フィボナッチ数列を出力するPythonのコードを実行してみました。
def fibonacci(n):
  """
  フィボナッチ数列のn番目の数字を返す関数

  Args:
    n: 数列のn番目 (整数)

  Returns:
    フィボナッチ数列のn番目の数字 (整数)
  """
  if n <= 1:
    return n
  else:
    return fibonacci(n-1) + fibonacci(n-2)

# フィボナッチ数列の最初の10個の数字を計算して出力
for i in range(10):
  print(fibonacci(i))


空のAPEXアプリケーションを作成し、ホーム・ページにPyodideを組み込んでいます。


コードを入力するページ・アイテムとしてP1_CODEを作成しています。タイプテキスト領域としています。


Pythonのコードの出力は、ページ・アイテムP1_OUTPUTに出力します。タイプテキスト領域としています。


ボタンとしてRUNCLEARを作成しています。ボタンRUNをクリックすることにより、P1_CODEに記述したPythonのコードを実行します。ボタンCLEARは、ページ・アイテムP1_CODEP1_OUTPUTをクリアします。

両方のボタン共に、動作は動的アクションにて定義しています。


Pyodideのロードと初期化を行なう設定を追加します。

ページ・プロパティJavaScriptファイルURLに以下を記述します。2024年11月11日現在では、v0.26.3が最新の模様です。

https://cdn.jsdelivr.net/pyodide/v0.26.3/full/pyodide.js

ファンクションおよびグローバル変数の宣言に以下を記述します。
var pyodide;

async function main() {
  pyodide = await loadPyodide();

  /*
   * 追加のライブラリの読み込みの例
   * Ref:
   * https://pyodide.org/en/stable/usage/packages-in-pyodide.html
   */
  await pyodide.loadPackage('scikit-learn');

  pyodide.setStdout({
    batched: (msg) => {
        let smsg = apex.item("P1_OUTPUT").getValue();
        apex.item("P1_OUTPUT").setValue(`${smsg}\n${msg}`);
    }
  });

  // Pyodide is now ready to use...
  console.log(pyodide.runPython(`
    import sys
    sys.version
  `));
};
ページ・ロード時に実行に以下を記述します。

main();

以上の設定により、ページ・ロード時にPyodideが初期化されます。


ボタンRUNを押した時に、ページ・アイテムP1_CODEに記述されたPythonのコードを実行します。

ボタンRUNTRUEアクションとしてJavaScriptコードの実行を選択し、設定コードとして以下を記述します。

pyodide.runPython(apex.items.P1_CODE.value);


ボタンCLEARを押した時に、ページ・アイテムP1_CODEP1_OUTPUTクリアします。


簡単ですが、以上でアプリケーションは完成です。

このAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/pyodide-on-apex.zip

Pythonのコードは単にテキスト領域に記述していますが、apex.worldに載っているMonaco Editorのプラグインを代わりに使うと、Pythonのコードも書きやすくなるかと思います。

Oracle APEXのアプリケーション作成の参考になれば幸いです。

2024年11月6日水曜日

Oracle APEX 24.1のAPEXアシスタントをデバッグする

Oracle APEX 24.1で新たにAPEXアシスタントが追加されました。APEXアプリケーションの作成やSQLの作成を、AIが手伝ってくれます。

そのAPEXアシスタントですが、しばしば以下のように応答することがあります。

「申し訳ありませんが、よく理解できませんでした。質問を言い換えていただけますか?」


以下より、APEXアシスタントが理解できなかった生成AIからの応答を確認する手順を紹介します。

APEXアシスタントは、ユーザー・ディクショナリ・キャッシュの情報に依存しているため、デバッグの前にキャッシュを更新しておきます。

管理メニューのサービスの管理を開きます。


画面右のメタデータの管理に含まれる、データ・ディクショナリ・キャッシュを開きます。


APEXアシスタントのでの利用に限れば統計情報は参照しないはずなので、キャッシュのリフレッシュのみを実行します。


リフレッシュが完了したら、アプリケーション・ビルダーからAPEXアプリケーションの作成を開始します。

アプリケーションの作成を開きます。


URLをコピーし、同じページを新しいタブまたはウィンドウとして開きます。


(同じAPEXセッションで)アプリケーション・ビルダーの画面が開きます。

開発中のAPEXアプリケーションは、開発者ツールバーからデバッグ・モードを設定できます。アプリケーション・ビルダーなどのAPEXが提供しているツールは開発者ツールバーが表示されないため、管理メニューよりデバッグ・モードを設定します。

管理メニューのアクティビティのモニターを開きます。


セッションアクティブ・セッションを開きます。


現在、アクティブなセッションが一覧されます。

URLの引数session=に現れているセッションIDのリンクを開きます。


デバッグ・レベルAPEX Traceまで上げて、変更の適用をクリックします。

詳細なログが出力されるため、パフォーマンスには大きな影響があります。


APEXアシスタントが開いてる画面で、JavaScriptコンソールを開きます。Chromeであれば、表示 -> 開発 / 管理 -> JavaScriptコンソールです。


生成AIを使用したアプリケーションの作成をクリックします。


JavaScriptコンソールに表示されている警告表示をあらかじめ消去して、APEXアプリケーションを作成するプロンプトを入力します。

生成AIの応答を確認するだけなので、デフォルトで案内されているプロンプトをそのまま入力してみます。


生成AIが返したブループリント(JSONの応答)やビュー識別子が、JavaScriptコンソールに出力されます。


JavaScriptコンソールに表示されるView Identifierは、デバッグ・レベルを設定したセッションの詳細画面のページ・ビューからアクセスできます。

ページ・ビューの一覧を更新するため、ページは再ロードします。


生成AIとのやり取りを含んだ詳細なデバッグ・メッセージを参照できます。


クエリ・ビルダーも同様の手順でデバッグ・メッセージを取得できます。

APEXアシスタントを呼び出す前に、セッションのデバッグ・レベルAPEX Traceに変更し、JavaScriptコンソールを開きます。


APEXアシスタントを開き、SQLを修正するプロンプトを入力します。

生成AIからのレスポンスを受け取ると、JavaScriptコンソールにサーバー側のログのView Identifierが表示されます。


セッションの詳細を再ロードし、ページ・ビューの一覧を更新します。


生成AIとのやり取りを含んだ詳細なデバッグ・メッセージを参照できます。


APEXのアプリケーション開発者がAPEXの開発ツール自体のデバッグを行なうことは稀だとは思います。ただし、同じ手順を一般ユーザーのセッションのデバッグに使うことができるので、覚えておくと便利な手順です。

今回の記事は以上になります。

2024年11月1日金曜日

Oracle APEXのコンポーネント表示時に実行されるSQLの処理時間を確認する

Oracle APEXのコンポーネント表示時に実行されるSQLの処理時間を確認する方法を紹介します。

最初に以下のようなダッシュボードのページを例に取ります。


開発者ツールバーのデバッグを選択し、デバッグ・レベル情報(デフォルト)に変更します。


ブラウザの開発ツールに含まれるJavaScriptコンソールを開きます。以下はChromeで実行しています。


ページの再表示といったブラウザの操作を実施し、確認したいSELECT文を実行します。

JavaScriptコンソールに色々とメッセージが表示されます。


JavaScriptコンソールに出力されているメッセージの中に、以下のような記述が見つかります。

Server debug log message written: View Identifier = 49143, Level = 3. http://.....


http(s)://...で始まるURLを開くと、サーバー側で生成されたデバッグ・メッセージが表示されます。


デバッグ・メッセージの中に、コンポーネントが実行したSQLが見つかります。


経過や実行に処理時間が記載されていますが、SQL文が記載されている場所の時間はパースにかかった時間になり、SELECT処理全体の一部です。その後のfetch処理にかかっている時間も含めて、処理時間を確認します。

デバッグ・レベル完全トレースにすると、実行されているSELECT文の実行計画がデバッグ・メッセージとして出力されます。


その他にも詳細な情報が含まれますが、ページの表示には相当な時間がかかります。完全トレースはある程度、原因を絞り込んでから取得した方が効果的です。


ファセット検索のページでは、ファセットの選択ごとにサーバー側のデバッグ・メッセージへのリンクが出力されます。


対話グリッドのような遅延ロードをサポートしているコンポーネントは、リージョンをリフレッシュするときにSELECT文が実行されます。そのため、デバッグ・メッセージも出力されます。


JavaScriptコンソールに表示されるリンクをクリックすると、サーバー側の処理のデバッグ・メッセージが開くのは便利ですが、つねに新規のタブとして開く点には注意が必要です。

あまりタブが開きすぎないように、参照しなくなったタブは閉じるようにします。タブを閉じても、記録されたデバッグ・メッセージはデータベースに保存されているので、後から参照することもできます。特にページ・ビュー識別子を記録していれば、デバッグ・ログを一意に選択できます。そうでなくても、ページ番号、実行時刻(タイムスタンプ)およびパス情報などから、デバッグ・メッセージ探すことができます。


新たなタブで開いているデバッグ・メッセージの画面からアプリケーション・ビルダーを開いてしまうと、すでに開いているアプリケーション・ビルダーのセッションが無効になってしまいます。なので、そのような操作は行わない方がよいです。

今回の記事は以上です。