TF.js 実践開発レシピ

Pythonで学習した画像認識モデルをTensorFlow.js形式に変換し、Node.jsで動かす方法

Tags: TensorFlow.js, Node.js, モデル変換, 画像認識, Python

はじめに

PythonとTensorFlowやKerasを用いて画像認識モデルを開発されている多くのエンジニアの方が、そのモデルをWebブラウザやNode.js環境で利用したいという課題に直面されることがあります。TensorFlow.jsは、このような課題を解決するためのJavaScriptライブラリです。本記事では、Pythonで学習・保存した画像認識モデルをTensorFlow.js形式に変換し、特にサーバーサイドやデスクトップアプリケーションなどで利用可能なNode.js環境でそのモデルをロードして推論を実行する具体的な手順とコード例を詳細に解説します。

Pythonでのモデル開発経験をお持ちの読者にとって、TF.jsへの移行はモデル変換というステップが重要になります。この記事が、Pythonで培った機械学習の知識をJavaScript環境で活かすための一助となれば幸いです。

Pythonでのモデル準備とSavedModel形式での保存

まず、TensorFlow.jsで利用可能な形式に変換するためには、Python側でモデルを適切な形式で保存する必要があります。TensorFlow.js Converterがサポートする主な形式は以下の通りです。

これらの形式の中で、SavedModel形式が最も柔軟で推奨されています。SavedModel形式でモデルを保存するには、Kerasの model.save() メソッドを使用するのが一般的です。

# Python (TensorFlow/Keras) でのモデル保存例
import tensorflow as tf
from tensorflow import keras

# モデルの構築と学習(ここでは省略)
# model = ...

# SavedModel形式で保存
model.save('my_image_recognition_model/saved_model')

上記のコードは、学習済みのKerasモデルを ./my_image_recognition_model/saved_model ディレクトリにSavedModel形式で保存します。このディレクトリには、saved_model.pb ファイルや variables ディレクトリなどが含まれます。このディレクトリ全体が変換の対象となります。

TensorFlow.js Converter によるモデル変換

PythonでSavedModel形式で保存したモデルを、TensorFlow.jsがNode.jsやWebブラウザで読み込める形式に変換します。これには、tensorflowjs_converter というツールを使用します。

Converterのインストール

tensorflowjs_converter は、tensorflowjs pipパッケージに含まれています。以下のコマンドでインストールできます。

pip install tensorflowjs

モデルの変換実行

インストールが完了したら、以下のコマンドでSavedModel形式のモデルをTensorFlow.js形式に変換できます。

tensorflowjs_converter \
    --input_format=tf_saved_model \
    --output_format=tfjs_graph_model \
    --saved_model_tags=serve \
    ./my_image_recognition_model/saved_model \
    ./my_image_recognition_model_tfjs

各オプションの意味は以下の通りです。

コマンドが正常に実行されると、指定した出力ディレクトリ (./my_image_recognition_model_tfjs) 内に、model.json ファイルと、重みデータを含む複数の .bin ファイルが生成されます。これらのファイル群が、TensorFlow.jsでロードするモデルの実体となります。

量子化オプション (オプション)

モデルのサイズを削減し、推論速度を向上させるために、重みを量子化して保存することが可能です。変換時に以下のオプションを追加します。

tensorflowjs_converter \
    --input_format=tf_saved_model \
    --output_format=tfjs_graph_model \
    --saved_model_tags=serve \
    --quantization_bytes=2 \
    ./my_image_recognition_model/saved_model \
    ./my_image_recognition_model_tfjs_quantized

--quantization_bytes=2 は、重みを16ビット浮動小数点数(Float16)または16ビット整数に量子化することを意味します。--quantization_bytes=1 は8ビット整数に量子化します。量子化はモデルの精度に影響を与える可能性があるため、適用後は精度評価を行うことが推奨されます。

Node.js 環境でのモデルロードと推論実行

変換されたTensorFlow.js形式のモデルを、Node.js環境で利用します。

必要なライブラリのインストール

Node.jsでTensorFlow.jsを実行するには、@tensorflow/tfjs-node または @tensorflow/tfjs-node-gpu パッケージをインストールします。GPU版を使用すると、対応するGPU環境があれば推論を高速化できます。

# CPU版を使用する場合
npm install @tensorflow/tfjs-node

# GPU版を使用する場合 (GPU、CUDA Toolkit、cuDNNのインストールが必要)
npm install @tensorflow/tfjs-node-gpu

モデルのロード

@tensorflow/tfjs ライブラリをインポートし、tf.loadGraphModel または tf.loadLayersModel メソッドを使用して変換済みのモデルをロードします。SavedModelから変換したGraphModelの場合は tf.loadGraphModel を使用します。

// index.js
const tf = require('@tensorflow/tfjs-node');
const path = require('path');

async function loadModel(modelPath) {
  try {
    // model.jsonへのパスを指定
    const modelUrl = `file://${path.resolve(modelPath, 'model.json')}`;
    const model = await tf.loadGraphModel(modelUrl);
    console.log('Model loaded successfully.');
    return model;
  } catch (error) {
    console.error('Error loading model:', error);
    throw error;
  }
}

// モデルロード例
const modelDir = './my_image_recognition_model_tfjs'; // 変換したモデルのディレクトリ
// loadModel(modelDir).then(model => { ... });

tf.loadGraphModel は非同期関数であるため、async/await または .then() を使用して結果を待ちます。

推論用の画像データ準備

画像認識モデルで推論を行うためには、入力となる画像データをモデルが期待する形式(通常はバッチサイズ、高さ、幅、チャンネル数の4次元テンソル)に変換する必要があります。Node.js環境で画像ファイルを読み込み、テンソルに変換する一般的な方法を以下に示します。@tensorflow/tfjs-node は画像のエンコード/デコード機能も提供しています。

const fs = require('fs').promises;

async function loadImageAndProcess(imagePath) {
  try {
    const imageBuffer = await fs.readFile(imagePath);
    // 画像バッファをテンソルにデコード
    const imageTensor = tf.node.decodeImage(imageBuffer);

    // モデルの入力形状に合わせて前処理(例: リサイズ、正規化)
    // モデルが期待する入力形状はモデルのアーキテクチャに依存します。
    // ここでは一般的な画像分類モデルの入力形状 (1, height, width, channels) を想定します。
    const resizedImage = tf.image.resizeBilinear(imageTensor, [224, 224]); // 例: 224x224にリサイズ
    const normalizedImage = resizedImage.toFloat().div(255.0); // 例: 0-1の範囲に正規化
    const inputTensor = normalizedImage.expandDims(0); // バッチサイズ次元を追加

    imageTensor.dispose(); // 元のテンソルは不要になったら解放
    resizedImage.dispose(); // 元のテンソルは不要になったら解放
    normalizedImage.dispose(); // 元のテンソルは不要になったら解放

    return inputTensor;
  } catch (error) {
    console.error('Error processing image:', error);
    throw error;
  }
}

// 画像処理例
// const imagePath = './test_image.jpg';
// loadImageAndProcess(imagePath).then(inputTensor => { ... });

tf.node.decodeImage は、JPEGやPNGなどの画像ファイルバッファからテンソルを作成します。その後の処理(リサイズ、正規化など)は、Pythonでモデル学習時に行った前処理と同じ手順を適用する必要があります。PyTorchやTensorFlow/Kerasで画像データをどのように前処理していたかを確認し、それと同等の処理をTensorFlow.jsで行います。テンソルのメモリは自動的には解放されないため、不要になったテンソルは dispose() メソッドを呼び出して明示的に解放することがメモリ管理上重要です。

推論の実行

モデルと入力テンソルが準備できたら、model.predict() メソッドを使用して推論を実行します。

async function runInference(model, inputTensor) {
  try {
    console.log('Running inference...');
    const prediction = model.predict(inputTensor);
    console.log('Inference complete.');

    // 推論結果の処理(例: softmax、結果の取得)
    // predictionはテンソルです。結果を取得するには .data() または .array() を使用します。
    // .data() は TypedArray を返します(非同期)。
    // .array() は多次元配列を返します(非同期)。
    const predictionArray = await prediction.array();

    prediction.dispose(); // 推論結果テンソルを解放
    inputTensor.dispose(); // 入力テンソルを解放

    return predictionArray;
  } catch (error) {
    console.error('Error during inference:', error);
    throw error;
  }
}

// 推論実行と結果表示の統合例
async function main() {
  const modelDir = './my_image_recognition_model_tfjs';
  const imagePath = './test_image.jpg'; // 推論対象の画像ファイルパス

  try {
    const model = await loadModel(modelDir);
    const inputTensor = await loadImageAndProcess(imagePath);
    const predictionResult = await runInference(model, inputTensor);

    console.log('Prediction result:', predictionResult);

    // 必要に応じて、推論結果 (predictionResult) をクラスラベルなどに変換する処理を追加

    model.dispose(); // モデルオブジェクトを解放 (アプリケーション終了時など)
  } catch (error) {
    console.error('Application failed:', error);
  }
}

main();

model.predict() も非同期関数です。推論結果はテンソルとして返されるため、JavaScriptのネイティブなデータ型(配列など)として取得するには、.data() または .array() メソッドを使用します。これらも非同期で実行されます。

実践的な考慮事項

まとめ

本記事では、Pythonで訓練した画像認識モデルをTensorFlow.js形式に変換し、Node.js環境で実行するための具体的なステップとコード例を解説しました。tensorflowjs_converter ツールを使ったモデル変換、@tensorflow/tfjs-node ライブラリを使ったNode.js環境でのモデルロード、画像データの前処理、そして推論実行までの一連の流れをご確認いただけたかと思います。

Pythonでの機械学習開発経験を活かしつつ、Node.jsという異なる実行環境でモデルを利用したいというニーズに対し、TensorFlow.jsは強力なソリューションを提供します。この知識は、サーバーサイドでの自動画像処理、デスクトップアプリケーションへのAI機能組み込みなど、様々な応用が可能となります。

今後は、Webブラウザでの実行方法、パフォーマンス最適化のテクニック(Web Workerの利用、モデルの量子化の詳細など)、特定のモデルアーキテクチャに特化した利用方法などに焦点を当てていくことも有効でしょう。この記事が、読者の皆様のTensorFlow.jsを用いた実践的な開発の一助となれば幸いです。