View in English

  • メニューを開く メニューを閉じる
  • Apple Developer
検索
検索を終了
  • Apple Developer
  • ニュース
  • 見つける
  • デザイン
  • 開発
  • 配信
  • サポート
  • アカウント
次の内容に検索結果を絞り込む

クイックリンク

5 クイックリンク

ビデオ

メニューを開く メニューを閉じる
  • コレクション
  • トピック
  • すべてのビデオ
  • 利用方法

その他のビデオ

  • 概要
  • トランスクリプト
  • コード
  • BNNS Graphの新機能

    BNNS Graph Builder APIにより、デベロッパは使い慣れたSwift言語を使用してオペレーションのグラフを記述し、前処理と後処理のルーチンや小規模な機械学習モデルを生成できるようになります。BNNSはグラフを実行前にコンパイルし、オーディオ処理などのリアルタイムのユースケースや遅延が許容されないユースケースをサポートします。このセッションでは、昨年のBitcrusherの例を再検討し、別個のPythonファイルに依存することなく、オーディオエフェクトを完全にSwiftで実装することで、Swiftコンポーネントを簡素化します。BNNS Graph Builder APIは、機械学習モデルへの送信に先立つ画像データの前処理にも適しています。セッションでは、アルファチャンネルのある画像から透明なピクセルをクリッピングするデモも行います。

    関連する章

    • 0:00 - イントロダクション
    • 3:12 - BNNSGraphの基本の確認
    • 6:15 - BNNSGraphBuilder
    • 16:58 - BNNSGraphBuilderの使用方法

    リソース

    • BNNS
    • Supporting real-time ML inference on the CPU
    • vImage.PixelBuffer
      • HDビデオ
      • SDビデオ

    関連ビデオ

    WWDC25

    • Appleプラットフォームでの機械学習/AIフレームワーク

    WWDC24

    • CPUでリアルタイムのML推論をサポート
  • このビデオを検索

    こんにちは Simon Gladmanです Appleの Vector and Numericsグループの所属です このグループはCPU上で 高性能かつ高エネルギー効率の 計算を実現する ライブラリスイートを提供しています これらのライブラリは画像処理、 信号処理、線形代数や 本日お話しする 機械学習をサポートします Basic Neural Network Subroutines(BNNS)は Appleが開発した機械学習ライブラリです これを使用するとCPUベースの 推論をアプリに追加でき オーディオ処理など 低レイテンシーが求められるリアルタイムの ユースケースに最適です これを使用することで 次のような機能を実装できます オーディオ分離による ボーカルの分離または削除 コンテンツに基づく オーディオの複数の領域への分割

    ある楽器の音色を別の楽器の音色に変える 音色変換の適用などです

    しかし BNNSは画像など 他のデータの処理にも 適しています そのため アプリに高性能の推論が 必要な場合は BNNSが最適です

    昨年Appleは 優れた新しいAPIである BNNSGraphを導入しましたが これにより BNNSのスピードとエネルギー効率が高まり 操作性も大幅に向上しました その際 私たちは BNNSGraphのデモを実施し このBitCrusher Audio Unitを作成しました

    それを使用して Logic Proや Garage Bandに独自のエフェクトを 簡単に追加できることを示しました

    今年は さらに優れたプログラミング インターフェイスをBNNSGraphに提供します これにより Swiftのパワーを活用して 推論用の小規模モデルを作成したり 前処理と後処理の処理グラフを 作成したりできます 本セッションでは BNNSGraphに新しく追加された BNNSGraphBuilderについてお話しします このセッションでは まず BNNSGraphについて簡単におさらいします BNNSGraphの概要、機械学習モデルと AIモデルを最適化する上での有用性、 導入方法を説明します 次にBNNSGraphBuilderを紹介します 実装に必要となる 簡単なワークフローを概説し イントロダクションの一部として 簡単なグラフの書き方を実演します BNNSGraphBuilderの紹介の後には 3つのデモを行います まず GraphBuilderを使って 画像の前処理を行います 次に MLモデルで生成したデータの後処理を 新しいAPIを使用して実行します 最後に 昨年のBitCrusherのデモの アップデートとして Swiftで処理グラフを作成します それでは早速始めましょう BNNSGraphはオーディオや 遅延の影響を 受けやすいその他のユースケースに最適です メモリ割り当てとマルチスレッディングを 制御できるためです それらの処理では カーネルコードへの コンテキストスイッチが発生し 処理が遅延する場合があります 昨年までは BNNSベースのネットワークの構築に 畳み込み、行列乗算、活性化などの 離散レイヤーを使用していたことと思います BNNSを使用して 既存のモデルを実装する場合は 各レイヤーを BNNSプリミティブとしてコーディングし すべての中間テンソルのコードを 記述する必要があります BNNSGraphは 複数のレイヤーとそれらの レイヤー間のデータフローで構成される グラフ全体を取得し それを1つの グラフオブジェクトとして使用します

    これは各レイヤーのコードを書く必要が ないことを意味します さらに 皆さんもユーザーも さらなる高速化と 優れたエネルギー効率の恩恵を受けられます

    BNNSGraphはモデル全体を認識するため 以前は不可能だった多くの最適化を 実行することができます これらの最適化で実行時間、メモリ使用量、 エネルギーコストを削減できます しかもこれらの最適化は無料で利用できます これらの最適化の詳細について モデルの各部分に沿って確認しましょう 最適化の1つは数学的変換です 例えば スライス処理の順序変更により BNNSで計算する必要がある対象を そのスライス内にある要素の サブセットのみにできます もう1つの最適化はレイヤー融合です 例えば 畳み込みレイヤーと 活性化レイヤーを融合させて 1つの処理にまとめることができます またコピー省略では スライス内の データのサブセットを参照することで スライス内のデータのコピーを回避できます 可能な場合 テンソルでメモリを共有すると BNNSGraphはメモリ使用量を最適化し 不要な割り当てを排除できます 最後に 重み再パッキング最適化では 重みが再パッキングされ キャッシュの局所性が向上します こうした最適化のメリットを得るために コードを記述する必要はなく そのままで適用されます 昨年導入されたファイルベースのAPIで グラフを作成するには CoreMLパッケージから始めます Xcodeは自動的にパッケージを mlmodelcファイルにコンパイルします 次にデベロッパが mlmodelcファイルから グラフを構築するコードを記述します 最後に グラフをラップする コンテキストを作成します 推論を実行するのはそのコンテキストです このアプローチは現在でも 既存の PyTorchモデルをプロジェクトに統合する 最善の方法です しかし 小規模なモデルや処理グラフの場合は より直接的なワークフローから メリットが得られる可能性があります グラフの個々の要素を Swiftで定義できるとしたらどうでしょうか それを実現するのが 今年Appleが導入する新しいAPIである BNNSGraphBuilderです

    新しいBNNSGraphBuilder APIでは 使い慣れたSwift言語で 処理グラフを記述できます 前処理と後処理のルーチンや 小規模な機械学習モデルを Swiftで直接記述できます そのSwiftコードから 中間ステップを経ずに1回の関数呼び出しで コンテキストを作成します Swiftを使って 他のコードのインラインで グラフを記述すると いくつかのメリットが直ちに得られます 使い慣れた言語を使用でき Swiftプロジェクトのコンパイル時に グラフの型チェックを実行できます 実行時に判明する値を グラフの生成前に Swiftとグラフの間で 共有することもできます 例えば 実行時のテンソルの形状が グラフを初期化する前にわかっていれば 形状をグラフのコードに直接渡すことができ 柔軟なサイズよりも高パフォーマンスな 静的サイズのメリットが得られます BNNSGraphBuilderでは 中間テンソルのクエリも実行でき 形状やデータ型などのプロパティを取得して グラフのデバッグに利用できます また 型テンソルを使うと Xcodeでオートコンプリートが可能になり ランタイムエラーの可能性を低減できます では次に新しい構文、 型チェック、新しいメソッドと演算子、 中間テンソルをクエリする方法などの これまで利用できなかった 各種の機能を見てみましょう 今年 BNNSGraphオブジェクトに makeContextという名前の 新しい型メソッドを追加しました この新しいメソッドは Swiftコードを グラフの実行に使用する 再利用可能なコンテキストに変換します 通常 このコンテキストは アプリの 起動中に一度だけ作成します さらに アプリは必要に応じて コンテキストを実行します これにより グラフを包括的に扱うことでもたらされる 最適化のメリットをアプリで活用できます 次は ライブコーディングで makeContextの動作を確認しましょう このメソッドはクロージャを受け取りますが このクロージャは グラフを構成する 一連の処理の定義に使用されます

    グラフは通常 1つ以上の引数を受け取ります ここでは クロージャのbuilderパラメータを 使用して 2つの引数xとyを 指定します

    パラメータはどちらも float値を持つ8要素のベクトルです この例で このグラフは 2つの入力引数について 要素ごとの乗算を実行します

    そして productという名前の出力引数に 結果を書き込みます このグラフはproductの平均値も計算し その値をmeanという名前の 2つ目の出力引数に書き込みます 最後にこのコードは それら2つの出力引数を返します

    デバッグを支援するために BNNSGraphBuilderが生成する 中間テンソルと出力テンソルの 詳細を出力できます

    この例の場合 平均値の形状が1になる つまり要素が1つだけ含まれるように 平均値の形状を出力しました コンテキストが作成されると コンテキストをクエリして 引数とその位置を取得できます ここで コードはコンテキストの 引数名のクエリを実行し グラフの出力と入力を表す テンソルの配列を作成します BNNSGraphは 出力が先に来て 入力がそれに続くように 引数名を並べ替えます

    BNNSGraphBuilder APIは 様々なデータソースの引数を 初期化する関数や データをコピーまたは参照する関数など 充実した一連の関数を提供します この例では Swift配列からの 入力を初期化します

    入力引数と出力引数を割り当てて executeFunctionを呼び出すと グラフが実行され 2つの出力に結果が入力されます

    そして 関数を実行することで 結果を出力できます

    BNNSGraphBuilderでできるのは 乗算して平均値を求めるだけではありません 次に BNNSGraphBuilderの概要を 手短に確認して この新しいAPIが提供する その他の処理を見てみましょう これらはBNNSGraphBuilderに 含まれているプリミティブの ほんの一部です 例えば行列の乗算、畳み込み、縮小処理、 gatherやscatterなどの処理、 パディング、形状変更、 転置などの処理があります 新しいAPIは多くの処理を 単純なSwift演算子としてサポートします 例えば乗算、加算、減算、除算などの 算術演算子があります また等しい、小なり、大なりなどの 要素ごとの比較演算子もあります さらに 要素ごとの論理演算子もあります APIの概要についての説明は以上です 次に GraphBuilder APIの 2つの機能について詳しく説明します これらは Swiftデベロッパにはお馴染みの 機能ですが 1つ目は強い型付けです 強い型付けは 実行時に発生する可能性があるエラーを コンパイル時に検出するのに役立ちます BNNSGraphBuilder APIは テンソルのデータ型が 指定された処理において 正しいことを保証します 実際の例で説明します この例で グラフは浮動小数点の底を 整数指数で累乗した 要素ごとの結果を返します

    グラフは整数指数をFP16に キャストすることでこの計算を実行できます キャストなしで実行しようとすると makeContextメソッドを コンパイルできません またこのグラフは mask0の値が mask1の対応する値よりも小さい場合 要素をゼロにして結果をマスクします 比較によって生成される テンソル要素はブール型であるため グラフは 結果に条件を乗算するために 別のキャストを実行します この場合も makeContextメソッドは キャスト処理がないとコンパイルされません このタイプのエラーは コンパイル時に検出できれば アプリを公開する前に修正できます 以上が強い型付けの例です 次に スライスを行う つまりテンソルの一部を選択するための BNNSGraphBuilderの アプローチを見てみましょう これも Swiftデベロッパにとって 非常に馴染みがあるものです

    スライスは テンソルの特定の部分に対する 窓のようなものです 例えば スライスは行列の1つの 列または行である場合があります BNNSGraphの素晴らしい点は スライスを既存のデータへの 参照として扱い データのコピーやメモリの割り当てによる オーバーヘッドを生じさせないことです テンソルのスライスは一般的な処理です 新しいGraphBuilder APIでは テンソルのスライスを ソース処理または デスティネーション処理として指定できます 例えば 画像を機械学習モデルに渡す前に 関心対象以外の領域を トリミングで除去するとします GraphBuilder APIを使用すると 昼食を楽しんでいる このリスの写真の 中心を起点とした正方形の領域を 簡単に選択できます ここでは2つのvImageピクセルバッファを 定義しますが ピクセルバッファには 画像のピクセルデータ、寸法、 ビット深度、チャンネル数が格納されます 1つ目のバッファであるsourceには リスの写真が含まれています 2つ目のピクセルバッファである destinationは 切り取った正方形を含みます 各ピクセルバッファには 赤、緑、青の3つのチャンネルがあり 各チャンネルは 32ビットの浮動小数点形式です vImageピクセルバッファの詳細については vImageのドキュメントをご覧ください 横および縦の余白により 640x640のトリミングが ソース画像の中央に確実に配置されます これは スライス処理を使用して トリミングを実行するグラフです まず sourceを引数として定義し 高さと幅を 柔軟なサイズとして指定します この場合は-1としていますが 負の値を渡すと BNNSGraphでは それらの寸法を任意の値にできます shapeの最後の値である3は 画像に赤、緑、青の3つのチャンネルを 含めることを指定する値です BNNSGraphBuilder APIでは スライス処理に Swiftの添え字を使用します この例では 新しい構造 SliceRangeを使用しています 縦寸法および横寸法の 両方の開始インデックスは 対応する余白値です 終了インデックスを負の余白値として 設定すると 終了インデックスが その寸法から余白を引いた 終了値であることを示します

    この例では チャンネルの寸法に沿って トリミングを行う必要がなく コードではfillAllを指定して 3つのチャンネルをすべて含めています

    今年は vImageピクセルバッファの 新しいメソッドも導入します withBNNSTensorメソッドでは メモリおよび サイズやチャンネル数などのプロパティを ピクセルバッファと共有する 一時的なBNNSTensorを作成できます コードが示すように この新しいメソッドでは 画像をグラフと簡単にやり取りできます メモリが共有されるため メモリのコピーや割り当てが必要なく 画像処理時に 優れたパフォーマンスが得られます executeFunctionメソッドが値を返すと destinationピクセルバッファに トリミングされた画像が含まれます 結果のコアグラフィック画像を 作成することで リスが適切にトリミングされたことを 確認できます GraphBuilderの新しい SliceRange構造のほかに テンソルのスライスのためのAPIは Swiftのすべての範囲型をサポートします そのため スライスに関する あらゆるニーズをカバーできます 以上 スライスの概要を説明しました ここまで BNNSGraphBuilderと その機能の一部を見てきました 次に ユースケースについて説明します BNNSGraphBuilderは MLモデルとの間で受け渡しされるデータの 前処理と後処理のための処理グラフを 構築する 優れたAPIです 前処理手法の1つは 画像のしきい値を設定することです つまり 連続階調画像を 黒または白のピクセルのみを含む バイナリ画像に変換することです GraphBuilderを使用して この手法を実装する方法を見てみましょう

    グラフの作成を開始する前に いくつかの vImageピクセルバッファを再度定義します 1つ目はソース画像の格納場所 2つ目は しきい値を設定した画像を 受け取るdestinationです どちらのピクセルバッファも シングル チャンネルの16ビット浮動小数点形式です グラフの最初のステップは 入力画像を表すソースの定義です このコードでは 画像サイズに負の値を渡し グラフがサポートする 任意の画像サイズを指定しますが ピクセルバッファに一致する FP16のデータ型を 指定しています 連続グレースケールピクセルの 平均値の計算では 単にmeanメソッドを呼び出します 要素ごとの大なり演算子は 対応するピクセルが 平均ピクセル値よりも 大きい値を持つ場合 しきい値が設定されたテンソルに1を入れ それ以外の場合は0を入れます 最後に グラフはブール値の1と0を destinationピクセルバッファの ビット深度にキャストします withBNNSTensorメソッドを再度使って 2つの画像バッファをコンテキストに渡し 関数を実行することで しきい値が設定された画像を生成します

    空を飛ぶ2羽のペリカンを写した この連続階調グレースケール画像を グラフに渡すと すべてのピクセルが黒または白である このしきい値が設定された画像が得られます BNNSGraphBuilder APIの もう1つのユースケースは 機械学習モデルの結果の後処理です

    例えば MLモデルの結果に softmax関数を適用し さらにtop-k処理を適用するとします ここで postProcess関数は 小規模のグラフを作成します グラフは この関数の入力テンソルに 基づいて 入力引数を宣言します 次に 入力引数にsoftmax処理を適用し さらに topk値とインデックスを計算します コードがtopk関数に渡すkパラメータは makeContextクロージャの外で 定義されていることに注意してください 最後に グラフはtopkの結果を返します グラフを定義した後 この関数は 結果を格納するテンソルを宣言し 出力テンソルと入力テンソルを コンテキストに渡します 最後に makeArrayメソッドが topk値とインデックスを Swift配列に変換し それらを返します GraphBuilder APIを使用した データの前処理と後処理の例を いくつか紹介しました 次に 昨年のBitCrusherのデモにおける Swiftの部分を BNNSGraphBuilderで アップデートしてみましょう MLベースのオーディオエフェクトの追加が BNNSGraphではどれほど簡単かご覧ください

    昨年は BNNSGraphを Audio Unit Extensionアプリに組み込んで デモを実施しました アプリのオーディオ信号にリアルタイムの BitCrusherエフェクトを追加し Swiftを使用して 同じエフェクトを正弦波に適用することで ユーザーインターフェイスに そのエフェクトを視覚表示しました 例えば この正弦波の場合 グラフは音の解像度を下げて サウンドに歪みを加えることができます 視覚表示は連続的な波ではなく 離散的な階段状の波として 正弦波を表示します また サウンドに豊かさを加えるために サチュレーションゲインも含めました 昨年のデモでは PyTorchコードに基づく CoreMLパッケージを使用しました この昨年のコードを 新しいGraphBuilder APIを使って Swiftで記述した 同じ機能を持つコードと比較してみましょう 左がPyTorchコードで 右が新しいSwiftコードです 明確な違いの1つは Swiftでは中間テンソルの定義に letやvarを使用することです そのため 中間テンソルが 不変か可変かを自由に決定できます 利用可能な すべての処理にアクセスするために 追加のインポートは必要ありません さらに この場合のtanhなどの処理は 自由関数ではなくテンソルのメソッドです

    要素ごとの算術演算子については 2つのアプローチ間で違いはありません

    もう1つの違いは makeContextクロージャが 複数の出力テンソルを返せる点であり 常に配列が返されます Swift/GraphBuilderでは makeContextクロージャ内で 入力引数を定義できます 既存のPyTorchモデルがある場合は そのコードと既存のファイルベースのAPIを 使用することをお勧めします ただし 新しいプロジェクトの場合 新しいGraphBuilder APIを使用すると Swiftでグラフを記述でき この比較が示すように PyTorchで記述する場合の一連の処理と 同様の構造を持つグラフを作成できます さらに BNNSGraphの 16ビットサポートのデモを行い この例では FP32処理よりも 大幅に高速であることを示します ここでもSwiftコードは同じですが 今回は型エイリアスを使用して 精度を指定しています

    型エイリアスを変更し グラフで FP32ではなくFP16を使用するようにします この例では FP16は 同等のFP32のコードよりもはるかに高速です 全体をまとめると BNNSGraphBuilderの 使いやすいSwift向けAPIでは パフォーマンスとエネルギー効率に優れた モデルと処理グラフを記述できます このAPIは 遅延の影響を受けやすい リアルタイムのユースケースに最適ですが プログラミングインターフェイスが 使いやすいため 幅広いアプリに使用できます また Appleのドキュメントページを ぜひご覧ください BNNSGraphとBNNSGraphBuilderについての 参考資料が数多く掲載されています ファイルベースのAPIや C言語でのBNNSGraphの使用に関する 昨年のビデオもぜひご覧ください

    ご視聴ありがとうございました

    • 8:31 - Introduction to BNNSGraphBuilder

      import Accelerate
      
      
      
      func demo() throws {
      
          let context = try BNNSGraph.makeContext {
              builder in
           
              let x = builder.argument(name: "x",
                                       dataType: Float.self,
                                       shape: [8])
              let y = builder.argument(name: "y",
                                       dataType: Float.self,
                                       shape: [8])
              
              let product = x * y
              let mean = product.mean(axes: [0], keepDimensions: true)
              
              // Prints "shape: [1] | stride: [1]".
              print("mean", mean)
              
              return [ product, mean]
          }
          
          var args = context.argumentNames().map {
              name in
              return context.tensor(argument: name,
                                    fillKnownDynamicShapes: false)!
          }
          
          // Output arguments
          args[0].allocate(as: Float.self, count: 8)
          args[1].allocate(as: Float.self, count: 1)
          
          // Input arguments
          args[2].allocate(
              initializingFrom: [1, 2, 3, 4, 5, 6, 7, 8] as [Float])
          args[3].allocate(
              initializingFrom: [8, 7, 6, 5, 4, 3, 2, 1] as [Float])
      
              try context.executeFunction(arguments: &args)
          
          // [8.0, 14.0, 18.0, 20.0, 20.0, 18.0, 14.0, 8.0]
          print(args[0].makeArray(of: Float.self))
          
          // [15.0]
          print(args[1].makeArray(of: Float.self))
          
          args.forEach {
              $0.deallocate()
          }
      }
    • 12:04 - Strong typing

      // Performs `result = mask0 .< mask1 ? bases.pow(exponents) : 0
      
      let context = try BNNSGraph.makeContext {
          builder in
          
          let mask0 = builder.argument(dataType: Float16.self,
                                       shape: [-1])
          let mask1 = builder.argument(dataType: Float16.self,
                                       shape: [-1])
          
          let bases = builder.argument(dataType: Float16.self,
                                       shape: [-1])
          let exponents = builder.argument(dataType: Int32.self,
                                           shape: [-1])
          
          // `mask` contains Boolean values.
          let mask = mask0 .< mask1
          
          // Cast integer exponents to FP16.
          var result = bases.pow(y: exponents.cast(to: Float16.self))
          result = result * mask.cast(to: Float16.self)
          
          return [result]
      }
    • 14:15 - Slicing

      let srcImage = #imageLiteral(resourceName: "squirrel.jpeg").cgImage(
          forProposedRect: nil,
          context: nil,
          hints: nil)!
      
      var cgImageFormat = vImage_CGImageFormat(
          bitsPerComponent: 32,
          bitsPerPixel: 32 * 3,
          colorSpace: CGColorSpaceCreateDeviceRGB(),
          bitmapInfo: CGBitmapInfo(alpha: .none,
                                   component: .float,
                                   byteOrder: .order32Host))!
      
      let source = try vImage.PixelBuffer(cgImage: srcImage,
                                          cgImageFormat: &cgImageFormat,
                                          pixelFormat: vImage.InterleavedFx3.self)
      
      let cropSize = 640
      let horizontalMargin = (source.width - cropSize) / 2
      let verticalMargin = (source.height - cropSize) / 2
      
      let destination = vImage.PixelBuffer(size: .init(width: cropSize,
                                                       height: cropSize),
                                           pixelFormat: vImage.InterleavedFx3.self)
      
      let context = try BNNSGraph.makeContext {
          builder in
          
          let src = builder.argument(name: "source",
                                     dataType: Float.self,
                                     shape: [ -1, -1, 3])
          
          let result = src [
              BNNSGraph.Builder.SliceRange(startIndex: verticalMargin,
                                           endIndex: -verticalMargin),
              BNNSGraph.Builder.SliceRange(startIndex: horizontalMargin,
                                           endIndex: -horizontalMargin),
              BNNSGraph.Builder.SliceRange.fillAll
          ]
          
          return [result]
      }
      
      source.withBNNSTensor { src in
          destination.withBNNSTensor { dst in
              
              var args = [dst, src]
              
              print(src)
              print(dst)
              
              try! context.executeFunction(arguments: &args)
          }
      }
      
      let result = destination.makeCGImage(cgImageFormat: cgImageFormat)
    • 17:31 - Preprocessing by thresholding on mean

      let srcImage = #imageLiteral(resourceName: "birds.jpeg").cgImage(
          forProposedRect: nil,
          context: nil,
          hints: nil)!
      
      var cgImageFormat = vImage_CGImageFormat(
          bitsPerComponent: 16,
          bitsPerPixel: 16,
          colorSpace: CGColorSpaceCreateDeviceGray(),
          bitmapInfo: CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder16Little.rawValue |
                                   CGBitmapInfo.floatComponents.rawValue |
                                   CGImageAlphaInfo.none.rawValue))!
      
      let source = try! vImage.PixelBuffer<vImage.Planar16F>(cgImage: srcImage,
                                                             cgImageFormat: &cgImageFormat)
      let destination = vImage.PixelBuffer<vImage.Planar16F>(size: source.size)
      
      let context = try BNNSGraph.makeContext {
          builder in
          
          let src = builder.argument(name: "source",
                                     dataType: Float16.self,
                                     shape: [-1, -1, 1])
          
          let mean = src.mean(axes: [0, 1], keepDimensions: false)
          
          let thresholded = src .> mean
          
          let result = thresholded.cast(to: Float16.self)
          
          return [result]
      }
      
      source.withBNNSTensor { src in
          destination.withBNNSTensor { dst in
              
              var args = [dst, src]
              
              try! context.executeFunction(arguments: &args)
          }
      }
      
      let result = destination.makeCGImage(cgImageFormat: cgImageFormat)
    • 19:04 - Postprocessing

      func postProcess(result: BNNSTensor, k: Int) throws -> ([Float32], [Int32]) {
          
          let context = try BNNSGraph.makeContext {
              builder in
              
              let x = builder.argument(dataType: Float32.self,
                                       shape: [-1])
              
              let softmax = x.softmax(axis: 1)
              
              let topk = softmax.topK(k, axis: 1, findLargest: true)
              
              return [topk.values, topk.indices]
          }
          
          let indices = context.allocateTensor(argument: context.argumentNames()[0],
                                               fillKnownDynamicShapes: false)!
          let values = context.allocateTensor(argument: context.argumentNames()[1],
                                              fillKnownDynamicShapes: false)!
          
          var arguments = [values, indices, result]
          
          try context.executeFunction(arguments: &arguments)
          
          return (values.makeArray(of: Float32.self), indices.makeArray(of: Int32.self))
      }
    • 21:03 - Bitcrusher in PyTorch

      import coremltools as ct
      from coremltools.converters.mil import Builder as mb
      from coremltools.converters.mil.mil import (
          get_new_symbol
      )
      
      import torch
      import torch.nn as nn
      import torch.nn.functional as F
      
      class BitcrusherModel(nn.Module):
          def __init__(self):
              super(BitcrusherModel, self).__init__()
      
          def forward(self, source, resolution, saturationGain, dryWet):
              # saturation
              destination = source * saturationGain
              destination = F.tanh(destination)
      
              # quantization
              destination = destination * resolution
              destination = torch.round(destination)
              destination = destination / resolution
      
              # mix
              destination = destination * dryWet
              destination = 1.0 - dryWet
              source = source * dryWet
              
              destination = destination + source
              
              return destination
    • 21:03 - Bitcrusher in Swift

      typealias BITCRUSHER_PRECISION = Float16
          
      let context = try! BNNSGraph.makeContext {
          builder in
          
          var source = builder.argument(name: "source",
                                        dataType: BITCRUSHER_PRECISION.self,
                                        shape: [sampleCount, 1, 1])
          
          let resolution = builder.argument(name: "resolution",
                                            dataType: BITCRUSHER_PRECISION.self,
                                            shape: [1, 1, 1])
          
          let saturationGain = builder.argument(name: "saturationGain",
                                                dataType: BITCRUSHER_PRECISION.self,
                                                shape: [1, 1, 1])
          
          var dryWet = builder.argument(name: "dryWet",
                                        dataType: BITCRUSHER_PRECISION.self,
                                        shape: [1, 1, 1])
          
          // saturation
          var destination = source * saturationGain
          destination = destination.tanh()
          
          // quantization
          
          destination = destination * resolution
          destination = destination.round()
          destination = destination / resolution
          
          // mix
          destination = destination * dryWet
          dryWet = BITCRUSHER_PRECISION(1) - dryWet
          source = source * dryWet
          
          destination = destination + source
          
          return [destination]
      }
    • 22:34 - Changing precision

      typealias BITCRUSHER_PRECISION = Float16
          
      let context = try! BNNSGraph.makeContext {
          builder in
          
          var source = builder.argument(name: "source",
                                        dataType: BITCRUSHER_PRECISION.self,
                                        shape: [sampleCount, 1, 1])
          
          let resolution = builder.argument(name: "resolution",
                                            dataType: BITCRUSHER_PRECISION.self,
                                            shape: [1, 1, 1])
          
          let saturationGain = builder.argument(name: "saturationGain",
                                                dataType: BITCRUSHER_PRECISION.self,
                                                shape: [1, 1, 1])
          
          var dryWet = builder.argument(name: "dryWet",
                                        dataType: BITCRUSHER_PRECISION.self,
                                        shape: [1, 1, 1])
          
          // saturation
          var destination = source * saturationGain
          destination = destination.tanh()
          
          // quantization
          
          destination = destination * resolution
          destination = destination.round()
          destination = destination / resolution
          
          // mix
          destination = destination * dryWet
          dryWet = BITCRUSHER_PRECISION(1) - dryWet
          source = source * dryWet
          
          destination = destination + source
          
          return [destination]
      }

Developer Footer

  • ビデオ
  • WWDC25
  • BNNS Graphの新機能
  • メニューを開く メニューを閉じる
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    メニューを開く メニューを閉じる
    • アクセシビリティ
    • アクセサリ
    • App Extension
    • App Store
    • オーディオとビデオ(英語)
    • 拡張現実
    • デザイン
    • 配信
    • 教育
    • フォント(英語)
    • ゲーム
    • ヘルスケアとフィットネス
    • アプリ内課金
    • ローカリゼーション
    • マップと位置情報
    • 機械学習とAI
    • オープンソース(英語)
    • セキュリティ
    • SafariとWeb(英語)
    メニューを開く メニューを閉じる
    • 英語ドキュメント(完全版)
    • 日本語ドキュメント(一部トピック)
    • チュートリアル
    • ダウンロード(英語)
    • フォーラム(英語)
    • ビデオ
    Open Menu Close Menu
    • サポートドキュメント
    • お問い合わせ
    • バグ報告
    • システム状況(英語)
    メニューを開く メニューを閉じる
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles(英語)
    • フィードバックアシスタント
    メニューを開く メニューを閉じる
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program(英語)
    • News Partner Program(英語)
    • Video Partner Program(英語)
    • セキュリティ報奨金プログラム(英語)
    • Security Research Device Program(英語)
    Open Menu Close Menu
    • Appleに相談
    • Apple Developer Center
    • App Store Awards(英語)
    • Apple Design Awards
    • Apple Developer Academy(英語)
    • WWDC
    Apple Developerアプリを入手する
    Copyright © 2025 Apple Inc. All rights reserved.
    利用規約 プライバシーポリシー 契約とガイドライン