Work2: 物体検出デモについて
はじめに
機械学習といえば画像認識みたいなところがあると思いますが何故なんでしょうね
というわけで物体検出デモ作ってみました 普段テーブルデータばかりさわってるので、たまに画像やる度にSOTAが劇的に更新されていて驚きます デモ用にサンプル画像を4つ置いてありますが、ユーザーがドロップした画像に対しても物体検出できるようにしてあります 画像だと自前のデータで遊んでもらえるのが良いところですよね
ブラウザで推論する理由
機械学習システムの場合、推論をサーバーでやるかブラウザでやるかで悩むことは結構あると思います
- サーバ推論: 画像をサーバに送り、サーバ上の GPU/CPU で推論し、結果を返す
- ブラウザ推論: モデルを閲覧者のブラウザに読み込み、端末内で推論する
このデモではブラウザ推論を選びましたが、理由としては以下の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 Commons | CC 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: q8 | GPU は精度重視、CPU はサイズ圧縮重視 |
設計の軸は「ブラウザ内で完結し、閲覧者が気軽に試せる」ことです。サーバを持たないことでプライバシーと運用コストの問題を同時に解決し、CDN ロード + 遅延初期化でサイト全体への影響をゼロにしています。モデル選定では「ブラウザで確実に動く」ことを最優先し、理論上の性能より実行環境の互換性を取りました
関連記事
- 都道府県別コロプレス図の採用技術・アーキテクチャ・データソース — もう 1 つのポートフォリオ作品(データ可視化)の技術解説
お仕事募集しています
案件相談はX DM (@studio_haneya) まで