Developer Week 2024中に、私たちはプライベートベータ版でAI顔トリミングを導入しました。この機能は、検出された顔の周囲の画像を自動的にクロップし、今後のAI画像操作機能スイートの中で、初のリリースとなります。
AIによる顔トリミングが、誰でもImagesで利用可能になりました。この機能を一般提供するために、私たちはCPUベースのプロトタイプからWorkers AIのGPUベースの実装に移行し、大規模な使用を妨げる可能性のあるメモリリークなど、多くの技術的課題に対処できるようにしました。
写真提供:Suad Komardeen (@suadhamardeen) on Unsplash
未加工imagesを本番環境で利用可能なアセットに変換
顔認証の開発には、次の2つの特定のユースケースを念頭に置いて開発されたものがあります。
ソーシャルメディアプラットフォームやAIチャットボット。 Imagesを使うお客様から、編集されていない人物のimagesをきちんとした固定された形の小さなプロフィール写真に変換するお客様からのトラフィックが大量に発生しました。
電子商取引プラットフォーム。 同じ製品の写真がギャラリーページのサムネイルのグリッドに表示され、その後、さらに大きなビューで個々の製品ページに表示される場合があります。次の例は、クロップによってモデルのシャツから目を革新的に表現することができることを示しています。
写真提供:Media Modifier(@mediamodifier)、Unsplash
大量のメディアコンテンツを扱う場合、本番用にimagesを準備するのが面倒になることがあります。Imagesを使えば、同じ画像の複数バージョンを手動で生成したり保存する必要はありません。その代わり、お客様がオリジナルの画像のみを保存し続ける一方で、それぞれがお客様の仕様に合わせて最適化された各画像のコピーを提供します。
Cloudflareは、エンドユーザーへの画像配信方法を操作するためのパラメーターライブラリを提供しています。たとえば、画像の幅
と高さ
を100×100に設定することで、画像を正方形にトリミングすることができます。
デフォルトでは、imagesは元の画像の中心座標に向かってクロップされます。重力
パラメータは、焦点を変更することで、画像のクロップ方法に影響を与えることができます。画像の焦点として使用する座標を指定したり、Cloudflareが新しい焦点を自動的に決定できるようにしたりできます。
重力パラメータは、中心から離れた被写体の画像をクロップする際に便利です。写真提供:Andrew Small (@andsmall) on Unsplash
weight=auto
オプションは、サリエンシーアルゴリズムを使用して、画像の最も最適な焦点を選択します。顕著性の検出は、画像の視覚的に最も重要な部分を識別します。クロップ操作がこの関心のある領域に適用されます。当社のアルゴリズムは、色、輝度、テクスチャなどの視覚的キューを使用してimagesを分析しますが、画像内のコンテキストは考慮しません。この設定は、植物や高層ビルなどの無生物を含むimagesには適していますが、人の顔のような文脈的に意味のある被写体を確実に記録することはできません。
一方で、毎月4,500万件以上の一意のtransformationsにサービスを提供するAI chatbotプラットフォームなど、多くのアプリケーションのbandwidth使用の大部分を人のImagesが占めています。これは、開発者が人物のimagesを最適化する方法を改善する機会となりました。
AIによる顔クロップは、重み=顔
オプションを使用することで実行できます。これにより、どのピクセルが顔(または複数の顔)を表すかを自動的に検出し、その情報を使用して画像をクロップします。また、画像が顔に向けてどの程度密かにクロップするかにも影響することができます。 zoom
パラメータは、顔の周囲の領域がどれだけ画像に含まれるかという閾値をコントロールします。
モデルパイプラインは、プライバシーと機密性を最優先に、慎重に設計されています。この機能は、顔の識別や認識をサポートしていません。つまり、Cloudflareで最適化する際、2つの異なるimagesが同じ人物を表現していることや、1つのimages中の特定の人物を識別することは決してありません。代わりに、ImagesによるAIの顔クロップは、意図的に顔検出(人間の顔を表すピクセルの識別)に限定されています。
最初のステップは、自分たちの要件を満たすオープンソースモデルを選択することでした。舞台裏では、当社のAI顔クロップは、画像を人間の顔で分類する畳み込みニューラルネットワークモデルであるRetinaFaceを使っています。
ニューラルネットワークは、機械学習プロセスの一種で、人間の脳の働きに酷似しています。基本的なニューラルネットワークは、入力層、1つ以上の隠れ層、出力層の3つの部分で構成されています。各層のノードは、相互接続されたネットワークを形成し、データの伝送と処理を行います。各入力ノードは、次の層のノードと接続されています。
完全に接続された層は、ある層から次の層へとデータを渡します。
データは入力層を通過し、そこで分析され、最初の隠れ層に渡されます。計算はすべて隠れ層で行われ、結果は最終的に出力層を介して提供されます。
畳み込みニューラルネットワーク(CNN)は、人間が物事の見方を反映します。他人を目の当たりにする際、まず身体のプロフィールなどの抽象的特徴から始めて、目の色や口アプリケーションの形などの特定の特徴を処理します。
同様に、CNNは、最終結果を提供する前に、画像を断片的に処理します。前のレイヤーは、エッジ、色、線などの抽象的な特徴を探します。以降の層はより複雑になり、それぞれが人間の顔を構成するさまざまな特徴を識別する役割を担います。完全に接続された最後のレイヤーは、分類された特徴をすべて組み合わせて、画像全体の最終的な分類を1つ生成します。言い換えると、画像に人間の顔(例:目、鼻など)を検出した場合、CNNは、画像に人間の顔が含まれていると結論づけます。
画像に人が描かれているかどうか(画像分類)、および人が画像のどこにいるかを正確に判断できるモデル(オブジェクト検出)が必要でした。モデルを選択する際、当社が考慮した要素には次のようなものがあります:
WIDEFACEデータセットのパフォーマンスこれは最先端の顔検出ベンチマークデータセットであり、393,703のラベル付き顔、32,203のimagesが含まれており、スケール、ポーズ、オクルージョンの変動性が高いものになっています。
速度(1秒あたりのフレーム数)。 当社の画像最適化リクエストのほとんどは、(画像がストレージにアップロードされる前ではなく)配信時に発生するため、エンドユーザーへの配信パフォーマンスを優先しました。
モデルサイズ。モデルのサイズが小さいほど効率的に実行されます。
品質.小規模なモデルによるパフォーマンスの向上は、しばしば品質とトレードオフされることがよくあります。重要なのは、スピードと結果のバランスを取ることです。
最初のテストサンプルには、画像内の顔の数、顔サイズ、照明、シャープネス、アングルなどさまざまな要素を持つ500のimagesが含まれていました。BlazeFast、R-CNN(およびその後のFast R-CNNおよびFaster R-CNN)、RetinaFace、YOLO(You Only Look Once)など、さまざまなモデルをテストしました。
BlazeFastやR-CNNのような2段階の検出器は、画像内の潜在的なオブジェクトの位置を提案し、次に、それらの関心領域にあるオブジェクトを識別します。RetinaFaceやYOLOのような1段階検出器は、シングルパスでオブジェクトの位置とクラスを予測します。当社の調査では、2段階の検出方法は高い精度を提供できるものの、パフォーマンスが遅すぎて、実際のトラフィックでは実用的でないことがわかりました。一方、ワンステージ検出の手法は、効率的で高性能であるものの、高い精度を持つままでした。
最終的に、RetinaFaceを選択しました。99.4%と最も高い精度で動作し、同程度の値を持つ他のモデルよりも高速なパフォーマンスを示しました。当社では、RetinaFaceが複数のぼやけた顔を含むimagesでも、高い結果を提供することがわかりました。
写真提供:Ann Nygärd (@polmermaid) on Unsplash
推論(トレーニングモデルを使用して決定を行うプロセス)は、特に大きな画像では計算負荷が高くなる可能性があります。効率を維持するために、モデルにimagesを送信する際の最大サイズ制限を1024×1024ピクセルに設定しました。
これらの次元内のimagesは、分析のために直接モデルに渡されます。しかし、幅または高さのいずれかの寸法が1024ピクセルを超える場合は、代わりに推論画像を作成してモデルに送信します。元の画像と同じアスペクト比を持ち、どちらの寸法も1024ピクセルを超えない小さいコピーです。たとえば、125x2000の画像は64x1024にダウンスケールされます。このサイズ変更された一時バージョンを作成することで、モデルが分析する必要があるデータ量を減らし、処理の高速化を実現できます。
モデルは、すべての境界ボックス、つまり検出された顔を定義する画像内の領域を描画します。
そこから、画像の上部、左、下、右のエッジに最も近いボックスに基づいて、個々のボックスのすべてを包含する新しい外部境界ボックスを構築します。 .
左上
の点は、一番左のボックスのx
座標と、一番上のボックスのy
座標を使います。同様に、右下
の点は、一番右の箱のx
座標と、一番下のボックスのy
座標を使います。これらの座標は、同じ境界ボックスから取得することができます1つのボックスが上エッジと左エッジの両方に最も近い場合、その左上隅を外境界ボックスの左上の点として使用します。
AI顔クロップは、対策を表現する領域を識別し、最上、左、最右、最下の境界ボックスに基づいて、外境界ボックスと焦点を決定します。
外向きの境界ボックスを定義したら、画像をクロップする際に、その中心座標を焦点として使います。当社の実験では、検出された最大の顔の周囲に新しい焦点を設定するなどの他の方法と比較して、複数の顔を持つimagesでより優れた結果のバランスが得られることがわかりました。
切り取った画像領域は、外境界ボックスのサイズ(「d」)と、指定したズームレベル(「z」)に基づいて、(1÷z)×dの式で計算されます。zoom
パラメータは0から1の間の浮動小数点を受け付けます。ここで、zoom=1
の時に境界ボックスに画像をクロップし、0
に向けたズーム
トレンドとしてボックスの周囲の領域をより多く含めます。
2048x2048の元の画像を考えてみましょう。まず、顔検出のサイズ制限を満たすために、1024×1024の推論画像を作成します。次に、モデルの予測を使って外境界箱を定義します。この例では100x500を使っています。zoom=0.5
では、弊社の数式は、境界ボックスの2倍の大きさのクロップ領域を生成し、新しい幅("w")と高さ("h")の寸法は200x1000となります:
また、入力寸法と計算された寸法の間で、小さい方を選択するmin
関数を適用し、新しい幅と高さが画像自体の寸法を超えないようにします。つまり、ズームしすぎると、画像のエッジを超えて広がるクロップ領域を定義する代わりに、画像の幅または高さ全体を使用します。例えば、zoom=0.25
では、400x2000 の最初のクロップエリアが得られます。ここでは、計算された高さ(2000)が入力の高さ(1024)より大きいため、入力の高さを使用してクロップ領域を400×1024に設定します。
最後に、クロップ領域を元の画像のサイズに戻す必要があります。これは、より小さな推論画像が作成されるときにのみ適用されます。
当初、2048x2048の画像を2倍に縮小して、1024x1024の推論画像を作成しました。つまり、最終結果として、クロップ領域の寸法(最新の例では400×1024)に2を掛けて、最終結果として800×2048の切り分け画像を生成する必要があります。
ベータ版では、TensorFlow Rustを使用してモデルを書き直し、既存のRustベースのスタックと互換性を持たせました。推論のための計算(モデルが人間の顔の分類を行い、位置を決めるなど)はすべて、当社のネットワーク内のCPUで実行されました。
最初のころ、これはうまく機能し、ほぼリアルタイムの結果が見られました。
しかし、基礎となるImagesサービスのメモリ使用量の限界に近づいているという一貫したアラートを受信するようになったことで、実装の根本的な限界が明らかになりました。メモリ使用量の増加は、この時期の最近のデプロイメントと一致しませんでしたが、ある感覚から、顔クロップのコンピュート時間グラフがメモリ使用量の上昇と一致する上昇があることを発見しました。さらに追跡したところ、AIによる顔トリミングが問題の根本にあることが確認されました。
サービスがメモリ不足になると、プロセスを終了してメモリを解放し、システムがクラッシュするのを防ぎます。CPUベースの実装はRAMを他のプロセスと共有するため、他の画像最適化操作でエラーを引き起こす可能性があります。これを受けて、メモリアロケーターをglibc mallocからjemallocに切り替えました。これにより、実行時に使用するメモリを少なくすることができ、世界全体で約20TiBのRAMを節約することができました。また、CPU使用率を制限するために、顔トリミングリクエストの数を減らすことも始めました。
この時点で、AIの顔トリミングはすでに当社の社内利用と少数のベータ版のお客様に限定されていました。これらの手順は一時的にメモリ消費を減らしたものであり、グローバルなトラフィックを処理するには不十分だったため、長期的に使用するために、よりスケーラブルな設計を検討していました。
メモリ使用量のアラートが迫ってきているため、GPUベースのアプローチに移行する必要があるのは明らかでした。
CPUと異なり、GPUベースの実装は通常、メモリアクセスは専用で、より厳密に管理されるため、他のプロセスとの競合を回避できます。当社は、社内チームがGPUアクセスのモデルカタログにペイロードを統合するためのフレームワークを作成したWorkers AIチームと提携しました。
Workers AIモデルの中には、独自のスタンドアロンコンテナを持つものもあります。複数のコンテナにトラフィックをルーティングするにはコストがかかる可能性があるため、これはすべてのモデルで現実的ではありません。Workers AIでGPUを使う場合、データがネットワーク上を移動する必要があるため、遅延が発生する可能性があります。ここでは、モデルが大きくなるとネットワークトランスポートのオーバーヘッドがより顕著になるため、モデルのサイズが特に関係してきます。
これに対処するため、Workers AIはより小さなモデルを1つのコンテナに格納し、遅延に敏感なルーティングアルゴリズムを利用して、各ペイロードに対応する最適なインスタンスを特定します。つまり、トラフィックがない時はモデルをオフロードすることができるのです。
スケジューラーは、同じコンテナ内のモデルがGPUとどのように、いつ、やり取りするかを最適化するために使用されます。
RetinaFaceは最小GPUの1GBのVRAMで動作しています。十分に小さいため、実行時に同じ大きさのモデルと一緒にホットスワップできます。RetinaFaceモデルの呼び出しがある場合、Pythonコードが環境にロードされ、実行されます。
予想通り、Workers AIに機能を移行した後、メモリ使用量が大幅に減少しました。現在、Imagesサービスの各インスタンスは約150 MiBのメモリを消費しています。
この新しいアプローチでは、メモリリークがサービス全体の可用性に影響を与える可能性が低くなります。Workers AIはコンテナ内でモデルを実行するため、他のプロセスに影響を与えることなく、必要に応じてモデルを終了させ、再起動することができます。顔トリミングはImagesサービスとは別に実行されるため、再起動によって他の画像最適化操作が停止することはありません。
ベータ版の提供開始にあたり、Cloudflareブログを更新し、著者画像にAI顔トリミングを適用しました。
著者は、メインブログフィードと各ブログ記事の両方に円形プロフィール写真として表示される独自のimagesを提出できます。デフォルトでは、CSSはコンテナ内の画像を中央に集中させ、ヘッドの位置が中央に集中していないことがわかります。2つのプロフィール写真に異なる量のネガティブスペースが含まれる場合、作家の顔が異なる規模で表示される視覚的な不均衡が生じることもあります。
AIによる顔切り抜きでは、複数の作成者による投稿がよりバランスよく見えるようになります。
上記の例では、オースティンの元の画像が彼の顔の周りにしっかりとクロップされています。一方、テイラーの元の画像には、彼のトップ活用形と背景の大きなマージンが含まれています。その結果、オースティンの顔はテイラーよりも大きく、中心に近いように見えます。ブログのプロフィール写真にAIによる顔トリミングを適用したところ、両者の顔のサイズがより似ていて、共同作成された投稿のバランスと統一感が増しています。
多くの開発者がすでにImagesを使ってスケーラブルなメディアパイプラインを構築しています。私たちの目標は、単純な手作業のタスクを自動化することで、画像のワークフローを高速化することです。
Imagesチームにとって、これは始まりに過ぎません。背景の除去や生成のアップスケールなどの機能を含め、新しいAI機能をリリースする予定です。AI顔トリミングを無料で試すことは、Imagesダッシュボードで変換を有効化することで可能です。