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_STATUSにModel 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のアプリケーション作成の参考になれば幸いです。
完