studio haneya

Work2: 物体検出デモについて

はじめに

機械学習といえば画像認識みたいなところがあると思いますが何故なんでしょうね

というわけで物体検出デモ作ってみました 普段テーブルデータばかりさわってるので、たまに画像やる度にSOTAが劇的に更新されていて驚きます デモ用にサンプル画像を4つ置いてありますが、ユーザーがドロップした画像に対しても物体検出できるようにしてあります 画像だと自前のデータで遊んでもらえるのが良いところですよね


ブラウザで推論する理由

機械学習システムの場合、推論をサーバーでやるかブラウザでやるかで悩むことは結構あると思います

このデモではブラウザ推論を選びましたが、理由としては以下の3つが挙げられます

プライバシー: 画像がサーバに送信されないので、ユーザーの個人的な画像がサーバーに送られることがありません

サーバ不要: サーバを持たなくて良いので運用コストがかかりません。このサイトは Astro SSG(静的サイト生成)でCloudflare Pagesにデプロイしていますが、別途サーバーを用意する必要をなくすことができます

体験の即時性: アップロード待ち・サーバ応答待ちがなく、画像を選んだらすぐに推論が始まります

トレードオフとして、初回はモデルのダウンロード(数十 MB)が必要で、推論速度もGPUサーバに比べると遅くなります。しかしモデルはブラウザにキャッシュされるため2回目以降はダウンロード不要で、最近の端末なら数百ミリ秒〜数秒で推論が完了します。今回のようなデモであれば十分だし、ある程度の業務運用でも使用可能な選択肢だと思います

一方で、ブラウザ推論では機械学習モデルをユーザーブラウザに渡す必要があるのでリバースエンジニアリングしたりモデルを流用されてしまう可能性が生じます。wasmでビルドしたら解析しにくいかと思ったのですが、ネイティブのビルドと違って結構中身が分かっちゃうので全然対策になっていませんでした。秘密にしておきたい場合はサーバーサイドで実行しないと厳しいと思います。また、ユーザーPCでは動きそうにないぐらい必要スペックが高い場合もサーバー側でゴリゴリな環境を作れるサーバー推論を選ぶことになります。そんなわけで仕事ではサーバー推論を選ぶことが多いかもしれません


モデル選定: DETR(ResNet-50)

DETRとは

DETR(DEtection TRansformer)は、Facebook Researchが2020年に発表した物体検出モデルです。Resnet-50で2D特徴マップを作ってTransformerで物体検出するモデルです。Resnet-50自体は画像判別モデルですが、特徴量マップとして利用してしまうという面白いモデルです

YOLOなどの従来の物体検出モデルは多数の候補ボックスを生成してから重複を除去する後処理が必要でしたが、DETRはTransformerのセルフアテンションが候補間の重複を暗黙的に抑制するため後処理が要らなくなります。ブラウザ環境のように環境固有の最適化しにくい場面にも向いていますし、シンプルにそのまま使えば良いというのが好ましい特徴です

DETRは人・車・犬・猫・椅子・テレビなど日常的な80クラスの物体検出用のCOCOデータセットで学習されていますので、一般の方に使ってもらうデモとしてちょうど良いものだと思います。ライセンス的にもYOLOv5, v8がAGPL-3.0でソース開示義務があり仕事で使いにくいのに対して、DETRはApache 2.0なので使いやすい為、仕事で使う時に予行練習も兼ねてこちらにしています


推論ランタイム: Transformers.js

Transformers.js とは

今回使用しているDETRはTransformers.jsで動かしています

Transformers.js は、Hugging Faceが提供するJavaScript向けのML推論ライブラリでで、ONNX形式に変換されたHugging FaceモデルをブラウザやNode.jsで実行できます。内部的にはONNX Runtime Webを使っていて、WASMでビルドされているのでCPUだけでも高速動作してくれますし、WebGPUも有効になっているので端末とブラウザが対応していればGPUを使って動いてくれます

Pythonのtransformersライブラリと同様のpipeline() APIがあるので以下のように書けます

const detector = await pipeline('object-detection', 'Xenova/detr-resnet-50', {
  device: 'webgpu',
  dtype: 'fp16',
});
const results = await detector(imageSrc, { threshold: 0.2, percentage: true });

CDNランタイムロード

Transformers.jsはCDNが利用できるので、別途実行時ロードするようにしました

const RUNTIME_MODULE = 'https://cdn.jsdelivr.net/npm/@huggingface/transformers@3';
const mod = await import(/* @vite-ignore */ RUNTIME_MODULE);

WebGPU 優先・CPU フォールバック

WebGPUはユーザー環境によって使えたり使えなかったりしますので、WebGPUでやってみて、失敗したらCPUでやり直すという書き方にしています。丁寧に1ステップずつ確認していくこともできるのですが、手間がかかるだけであまり変わらないので、わりとベストプラクティスなんじゃないかと思ってます

WebGPU 利用可能?
  ├─ Yes → WebGPU (fp16) で推論を試行
  │    ├─ 成功 → 完了
  │    └─ 失敗 → CPU (q8) で再試行
  └─ No  → CPU (q8) で推論

WebGPUはまだ対応されていないブラウザが結構あり、またちゃんと動いてくれないこともちょくちょくあるようですが、対応しているデバイス&ブラウザではCPUより大幅に高速です。WebGPUでダメならやり直すとかってのをONNXだと結構コードが長くなりますが、Transformers.jsだと非常に簡潔に書けますのでそういう意味でもTransformers.jsは使い勝手が良いですね

データ型はWebGPUではfp16、CPUではq8にしています。fp16はGPUでよく使うデータ型でPascal以降のNvidia GPUで高速に計算できるデータ型ですね。CPUでは軽量さを重視してモデルサイズが小さくできるq8にしています。画像系のモデルは量子化しても精度が落ちにくいで量子化しやすいですね


UI 設計: 推論を1回で済ませる

閾値スライダーの設計

今回のDETRモデルでは閾値を設定して実行するとconfidenceが閾値以上のヒットだけ返してくれますが、今回はデモですから、低い閾値(0.2)で多めに取得しておいてスライダーで事後フィルタする設計にしました。デモですから動かせる部分があった方が面白いですよね

const RAW_THRESHOLD = 0.2;
const output = await detector(src, { threshold: RAW_THRESHOLD, percentage: true });

// 以降、閾値スライダーは detections.filter(d => d.score >= threshold) だけ

デモとしてさわってて面白いようにしたかったので、写真の右側に検出対象を一覧表示するようにして、マウスホバーすると写真上のバウンディングボックスが反応するようにしたので、すぐ近くにあるものが一覧上でも隣接するようにしてあります。具体的には、重なっているものをUnion-Findで連結成分に分けて、グループ単位でスコア降順で一覧というやり方です。これでもまあまあそれっぽくなるので一旦良しにしました

client:only=“react”

Astroでのハイドレーション指示に client:only="react" を使っています。コロプレス図はカッコいい地図が早く見えた方が嬉しいので client:visible にしていましたが、こっちは大したものが写らないので client:only にしています。client:only にするとサーバサイドレンダリング(SSG/SSR)を行わないので素のReactの動作になります


モデルのロードタイミング

モデルのダウンロードと初期化は遅延ロードにしています。ページを開いた時点ではモデルのロードを開始せず、ユーザーが画像を投入(ドロップまたはサンプル選択)した瞬間に初めてロードが走ります

ページ表示 → ドロップゾーン表示(軽量)
                ↓ 画像投入
         モデルロード開始(プログレス表示)
                ↓ ロード完了
         推論実行 → 結果描画

事前ロード(ページ表示と同時)にすると、MLデモに興味がないのにページを開いただけで数十MBのダウンロードが始まるのでやめました。モデルの重みはブラウザにキャッシュされるため、2回目以降の画像投入ではロードがスキップされ即座に推論が始まります


サンプル画像

デモにはサンプル画像を4枚同梱しています 以下から取得しています

ソースライセンス帰属表示
写真AC商用利用可・クレジット表示任意任意だが ID を記載
Wikimedia CommonsCC BY-SA 4.0著作者名・ライセンス表示が必要

クレジット情報はページ下部の <details> で折りたたんで表示し、邪魔にならない範囲でライセンス要件を満たすようにしました


まとめ

選定判断理由
モデルDETR(ResNet-50・COCO 80)NMS 不要でブラウザ実装が簡潔。ONNX Runtime Web との互換性
ランタイムTransformers.js(CDN ロード)ビルド非依存、サイト全体の初期ロードに影響なし
推論デバイスWebGPU 優先 → CPU フォールバック対応環境では高速に、非対応でも確実に動く
ハイドレーションclient:only="react"ブラウザ API 依存のため SSR 不可
閾値制御推論 1 回 + 事後フィルタスライダー操作で再推論を回避
データ型WebGPU: fp16 / CPU: q8GPU は精度重視、CPU はサイズ圧縮重視

設計の軸は「ブラウザ内で完結し、閲覧者が気軽に試せる」ことです。サーバを持たないことでプライバシーと運用コストの問題を同時に解決し、CDN ロード + 遅延初期化でサイト全体への影響をゼロにしています。モデル選定では「ブラウザで確実に動く」ことを最優先し、理論上の性能より実行環境の互換性を取りました


関連記事


お仕事募集しています
案件相談はX DM (@studio_haneya) まで


← 記事一覧に戻る