ハイライト
Apple は、Accelerate に BNNS 機械学習機能を追加し、simd.h の C++ テンプレート サポートを改善し、Apple Encrypted Archive を開始しました。これにより、開発者はシステム フレームワークを使用して、ハイ パフォーマンス コンピューティング、ベクトル化処理、および暗号化されたアーカイブを完了できるようになります。
主要内容
開発者が大規模なデータ処理に遭遇した場合、一般的なアプローチは、最初に Swift ループを作成することです。配列が小さい場合は問題ありません。入力が画像、オーディオ サンプル、行列、またはモデル テンソルになると、ループにかかる CPU 時間と消費電力が増加します。
手書きの最適化も面倒です。 CPU ベクトル命令、行列ライブラリ、画像形式、キャッシュの読み取りと書き込みを考慮し、Apple デバイス間のハードウェアの違いに対処する必要があります。コードが低いほど、ビジネス ロジックをハードウェア適応コードに書き込むのが簡単になります。
Accelerate はこのエントリの問題を解決します。 vDSP、vImage、vForce、BLAS、LAPACK、Sparse BLAS、Sparse Solvers、および BNNS を同じフレームワークに配置します。開発者はシステム API を呼び出し、システムは計算を適切な実装に転送する責任を負います。
このセッションでは、2 つの隣接するシーンも追加されました。
1 つ目は小さなベクトルの計算です。 simd.h は、CPU レジスタに収まる小さなベクトルと行列の処理に適しています。以前は、C++ テンプレート コードは、スカラー型、ベクトル長、および特定の SIMD 型の間で相互に変換する必要があり、その結果、多くの定型コードが発生していました。 WWDC 2021 では、新しいタイプ、特性、エイリアスが追加され、テンプレート コードの使用コストが削減されます。
2 つ目はアーカイブの暗号化です。ディレクトリをバックアップする場合、開発者は通常、ディレクトリをアーカイブ、圧縮、暗号化し、認証と署名を処理する必要があります。 Apple Encrypted Archive (AEA) は、圧縮、認証された暗号化、およびデジタル署名を単一の形式にまとめ、Swift および C API を提供します。
ニューラルネットワークからアーカイブまで、同じ目標
この講演には、Accelerate、simd.h、Apple Archive という 3 つのトピックが表面的にあります。共通点はパフォーマンスに対する感度です。
BNNS (Basic Neural Network Subroutines) は、CPU 上のトレーニングと推論プリミティブを担当します。 simd.h は、レジスタ レベルでの小さなベクトル計算を担当します。 AEA は、ディレクトリ全体が一度にメモリに置かれることを避けるために、ストリーミングの圧縮と暗号化を処理します。
Apple が行った変更は単純明快で、これらの低レベルの機能をシステム フレームワークに変えるというものです。開発者が入手できるのは、プロジェクトに直接導入できる一連のパフォーマンス ツールです。
詳細
関連するフレームワークを紹介する
(01:55) Accelerate は、macOS、iOS、iPadOS、watchOS、tvOS を含むすべての Apple プラットフォームで利用できます。講演では、Apple Silicon Macや最近のiPhoneやiPadで機械学習アクセラレータにアクセスする方法は、Accelerateを直接呼び出すか、Core MLなどの上位層フレームワークを通じて間接的に呼び出すことだと指摘した。
// Swift
import Accelerate
import AppleArchive
import Compression
import simd
// C / Objective-C / C++
#include <Accelerate/Accelerate.h>
#include <AppleArchive/AppleArchive.h>
#include <Compression/Compression.h>
#include <simd/simd.h>
キーポイント:
import Accelerate数値計算、信号処理、画像処理、BNNS 関連の機能を紹介します。import AppleArchiveアーカイブと Apple Encrypted Archive API の紹介。import Compression可逆圧縮機能を導入した AEA は、圧縮アルゴリズムとともに使用できます。import simdSwiftのSIMDの種類と機能を紹介します。- C、Objective-C、C++ は対応するヘッダー ファイルを使用します。
simd.h追加のフレームワークを追加する必要はありません。
BNNS は CPU 機械学習プリミティブを拡張します
(02:07) BNNS は、CPU 上で機械学習パフォーマンス プリミティブを提供し、トレーニングと推論をサポートします。これは MPS (Metal Performance Shaders) と同じレベルです。BNNS は CPU 指向で、MPS は GPU 指向です。 Core ML、Create ML、Vision、Natural Language などの上位層フレームワークは、これらのバックエンドで実行できます。
BNNS additions in this release:
- Layer types: embedding, random fill, quantization
- Optimizer: AdamW
- Activation functions: SiLU, HardSwish
- Arithmetic functions: ternary select, multiply add, element-wise minimum, element-wise maximum
- Layer fusions: convolution + quantization, fully connected + quantization, arithmetic + normalization
- Optimizer improvements: gradient clipping, AMSGrad support for Adam-based optimizers
キーポイント:
embedding、random fill、quantization今回の新しいレイヤータイプです。AdamWこれは、トレーニング シナリオのための新しいオプティマイザーです。SiLUそしてHardSwish新しい活性化関数です。ternary select、multiply add、要素ごとの最小値、および要素ごとの最大値により、算術層の機能が拡張されます。- レイヤー融合により、1 つのレイヤーの出力が後続のレイヤーの入力になることが可能になり、メモリへの書き込みと再度の読み取りのオーバーヘッドが削減されます。
- グラデーション クリッピングは、オプティマイザー内でもスタンドアロン関数としても使用できます。
simd を使用してブランチをマスク計算に変更します
(04:26) このスピーチでは、ベクトル化のアイデアを説明するためにカスタム アクティベーション関数を使用しています。それぞれのスカラーバージョンFloat3つの判断をしてください。データの量が大きくなるにつれて、このループは分岐や要素ごとの呼び出しによって制限されます。
import simd
public func swishharder_scalar (_ data: [Float]) -> [Float] {
return data.map { x in
if x <= -.pi { return 0 } // { 0 if x ≤ -π
if x <= .pi { return 2*exp(x) * (x + .pi)/2 } // f(x) = { 2eˣ * (x+π)/2 if -π < x < π
else { return 2*exp(x) } // { 2eˣ if π ≤ x
}
}
func swishharder_elementwise(_ x: SIMD8<Float>) -> SIMD8<Float> {
let y = 2*simd.exp(x)
let a = y.replacing(with: 0, where: x .<= -.pi)
let b = ((x + .pi)/2).replacing(with: 1, where: .pi .<= x)
return a*b
}
extension SIMD {
internal func store(
into buffer: UnsafeMutableBufferPointer<Scalar>,
startingAt offset: Int
) {
for i in 0 ..< scalarCount {
buffer[offset + i] = self[i]
}
}
}
public func swishharder_simd(_ data: [Float]) -> [Float] {
return Array<Float>(unsafeUninitializedCapacity: data.count) {
(buffer: inout UnsafeMutableBufferPointer<Float>, count: inout Int) in
for i in stride(from: 0, to: data.count, by: 8) {
let v = SIMD8(data[i ..< i+8])
let w = swishharder_elementwise(v)
w.store(into: buffer, startingAt: i)
}
count = data.count
}
}
キーポイント:
import simd導入SIMD8<Float>そしてsimd.exp。swishharder_scalar使用map入力配列を一度に 1 つずつ処理しますFloat。func swishharder_elementwise(_ x: SIMD8<Float>)入力を長さ 8 の SIMD ベクトルに置き換えます。let y = 2*simd.exp(x)8 つの要素の指数式を同時に評価します。y.replacing(with: 0, where: x .<= -.pi)比較結果を使用してマスクを生成し、以下に変換します。-π位置を次のように置き換えます0。((x + .pi)/2).replacing(with: 1, where: .pi .<= x)以上を入れてくださいπ位置を次のように置き換えます1。return a*b2 つのベクトル式を結合して、3 つの部分からなる関数の結果を取得します。store(into:startingAt:)SIMD ベクトルを出力バッファに書き込みます。stride(from:to:by: 8)ループを毎回 8 要素進めます。SIMD8<Float>幅に相当します。
トーク ライブを実行した後、SIMD バージョンはスカラー バージョンのほぼ 3 倍高速でした。ここで重要なのは、ifブランチはベクトル式とマスクされた置換に分割されます。
simd.h により C++ テンプレートの使用法が改善されました
(07:02) 今回の simd.h の主な改善点は、C++ テンプレートの使いやすさです。基礎となるスカラー型、ベクトル長、具体的な SIMD 型の間で変換を行うための新しい型と特性構造が追加されました。テンプレートコードのボイラープレートを減らすためにエイリアスを追加しました。
// Concepts shown in the session
vector and matrix types
Vector_t and Matrix_t aliases
get_traits struct
templated make functions
templated convert functions
キーポイント:
vectorそしてmatrixこの型は、スカラー型、ベクトル長から特定の SIMD 型を派生します。- 関連するメンバーは、比較結果に対応するアライメントされていないバージョンとマスク タイプにアクセスできます。
Vector_tそしてMatrix_tは、簡略化された構文のエイリアスです。get_traits特定の SIMD タイプから一般的な説明に戻ります。- テンプレートのバージョンを追加しました
makeそしてconvertでは、ターゲットの型がテンプレート パラメーターとして渡されるため、テンプレート コードへの組み込みが容易になります。 - 新しい分類機能には以下が含まれます
isfiniteそしてisinfのベクター バージョン。 - 新しい機能には、ガンマ関数と SIMD マトリックス トレース計算も含まれます。
Apple Encrypted Archive は、ストリームに圧縮、暗号化、署名を追加します
(08:32) Apple アーカイブはシステムのアップデートに使用されています。 macOS 11 では、圧縮コンテナーとアーカイブ形式が利用可能になります。 macOS 12 では、新しい暗号化 API が追加されています。
AEA は、デジタル署名、対称暗号化、パスワード暗号化、および公開キー暗号化のみの複数のプロファイルを提供します。各暗号化構成には、署名するかどうかのオプションがあります。圧縮はオプションであり、データは常に認証されます。
Apple Encrypted Archive profiles:
- digital signature without encryption
- symmetric encryption with or without a signature
- password encryption with or without a signature
- public key encryption with or without a signature
Command-line tools:
- compression_tool
- aea
- aa
キーポイント:
- デジタル署名は、ソフトウェアの更新など、コンテンツが機密ではないが改ざん防止が必要なシナリオに適しています。
- 対称暗号化では、安全に共有されたバイナリ キーが使用されます。
- パスワード暗号化では、入力としてパスワードが使用されます。
- 公開キー暗号化は、送信者と受信者の両方がキー ペアを使用するシナリオに適しています。
compression_tool圧縮されたアーカイブ部分を処理します。aea暗号化されたアーカイブを処理します。aaコンテナ全体を処理します。- Apple Archive フレームワークは、Swift および C API を提供します。
- API はストリームベース (ストリームベース) であり、シーケンシャル アクセスとランダム アクセスをサポートし、内部実装はマルチスレッドです。
Swift で AEA 暗号化ディレクトリを作成する
(12:14) 音声のデモでは、ディレクトリをアプリケーションにドラッグし、同じ名前を出力します。.aea書類。実装は、暗号化コンテキストの作成、ファイル ストリームと暗号化ストリームの作成、ディレクトリの内容のエンコード ストリームへの書き込みという 3 つのステップに分かれています。
/// Encrypts and archives the directory at the specified URL.
///
/// - Parameter sourceURL: The URL of the directory that the function encrypts and archives.
/// - Returns: A string containing the status message.
///
/// This function writes the result to `directory`. The archive shares the name
/// of the supplied directory with an `aea` extension.
static func encrypt(sourceURL: URL) -> ConsoleMessage {
// Verify that the URL path represents a directory.
if !sourceURL.hasDirectoryPath {
return ConsoleMessage(status: .error,
message: "The specified URL doesn't point to a directory.")
}
guard let source = FilePath(sourceURL) else {
return ConsoleMessage(status: .error,
message: "Unable to create file path from source URL.")
}
// Create the destination `FilePath`.
let destination = directory
.appending(sourceURL.lastPathComponent)
.appending(".aea")
let archiveDestination = FilePath(destination)
// Create the encryption context + setup credentials
let encryptionKey = SymmetricKey(size: .bits256)
let context = ArchiveEncryptionContext(
profile: .hkdf_sha256_aesctr_hmac__symmetric__none,
compressionAlgorithm: .lzfse)
do {
try context.setSymmetricKey(encryptionKey)
} catch {
return ConsoleMessage(status: .error,
message: "Error setting password (\(error).)")
}
// Create the file stream, encryption stream, archive encode stream.
guard
let archiveDestinationFileStream = ArchiveByteStream.fileStream(
path: archiveDestination,
mode: .writeOnly,
options: [ .create, .truncate ],
permissions: FilePermissions(rawValue: 0o644)),
let encryptionStream = ArchiveByteStream.encryptionStream(
writingTo: archiveDestinationFileStream,
encryptionContext: context),
let encoderStream = ArchiveStream.encodeStream(
writingTo: encryptionStream) else {
return ConsoleMessage(status: .error,
message: "Error creating streams.")
}
// Remember to close things in the correct order
defer {
try? encoderStream.close()
try? encryptionStream.close()
try? archiveDestinationFileStream.close()
}
// Encode all files in the target directory with the specifed fields.
do {
let fields = ArchiveHeader.FieldKeySet("TYP,PAT,DAT,UID,GID,MOD")!
try encoderStream.writeDirectoryContents(archiveFrom: source,
keySet: fields)
} catch {
return ConsoleMessage(status: .error,
message: "Error writing directory contents.")
}
if let url = URL(archiveDestination) {
NSWorkspace.shared.activateFileViewerSelecting([url])
}
let message = """
Encrypted with key '\(encryptionKey.base64Encoded)'.
Archived and encrypted to: \(archiveDestination.description).
"""
return ConsoleMessage(status: .encryptionSuccess(base64EncodedKeyData: encryptionKey.base64Encoded),
message: message)
}
キーポイント:
static func encrypt(sourceURL: URL)アーカイブが暗号化されるディレクトリ URL を受け取ります。sourceURL.hasDirectoryPathまず、入力がディレクトリを指していることを確認します。FilePath(sourceURL)URL を Apple Archive API で使用されるパスの種類に変換します。destination.appending(".aea")生成する.aea出力パス。SymmetricKey(size: .bits256)256 ビットの対称キーを作成します。ArchiveEncryptionContext(profile:compressionAlgorithm:)暗号化プロファイルと圧縮アルゴリズムを指定します。.hkdf_sha256_aesctr_hmac__symmetric__noneHKDF-SHA256、AES-CTR、HMAC、対称暗号化、デジタル署名なしを示します。.lzfseLZFSE 圧縮を使用するように指定します。context.setSymmetricKey(encryptionKey)キーを暗号化コンテキストに書き込みます。ArchiveByteStream.fileStreamターゲット ファイルに書き込まれるバイト ストリームを作成します。ArchiveByteStream.encryptionStreamファイル ストリームを暗号化ストリームにラップします。ArchiveStream.encodeStreamディレクトリエントリをアーカイブバイトにエンコードします。deferエンコーダ、暗号化、ファイルの順にストリームを閉じます。ArchiveHeader.FieldKeySet("TYP,PAT,DAT,UID,GID,MOD")アーカイブに保存されるファイルのフィールドを指定します。writeDirectoryContents(archiveFrom:keySet:)ディレクトリの内容をエンコード ストリームに書き込みます。encryptionKey.base64Encodedキー テキストを出力します。デモではこれを使用して後続の復号化を完了します。
重要ポイント
1. 画像エディターにバッチ フィルターを追加します。
- 何をすべきか: ユーザーが一度に複数の画像を選択し、バッチで形式変換、畳み込み、またはフィルター処理を実行できるようにします。
- 実行する価値がある理由: Accelerate の vImage は、フォーマット変換や畳み込みなどの繰り返し計算に適した画像処理ルーチンを提供します。
- 開始方法: まず画像をバッファにデコードし、次に Accelerate/vImage を使用して各画像を処理し、最後に結果をファイルに書き戻すか、インターフェイスに表示します。
2. オーディオ ツールにリアルタイム スペクトラム ビューを追加する
- 機能: 録音または再生中にスペクトル、ピーク、エネルギーの変化を表示します。
- 価値がある理由: Accelerate の vDSP は、信号処理に適した DFT および FFT ルーチンを提供します。
- 開始方法: オーディオ コールバックからサンプル ブロックを取得し、それらを vDSP に渡して FFT を実行し、周波数ドメインの結果を SwiftUI または Metal ビューにマッピングします。
3. CPU 推論パスをローカル モデルに追加する
- 内容: GPU またはニューラル エンジンが利用できない場合に、モデルに CPU フォールバック パスを提供します。
- 実行する価値がある理由: BNNS は CPU 上でトレーニングと推論のプリミティブを提供し、量子化、埋め込み、SiLU、HardSwish、AdamW などの機能を追加します。
- 開始方法: まずモデルに必要な層と活性化関数を特定し、次に BNNS に対応するプリミティブを使用して推論プロセスを組み立てます。既存の Core ML モデルの場合は、まず Instruments または関連ツールを使用してボトルネックを特定します。
4. SIMD 高速パスを科学計算アプリに追加する
- 内容: 正規化、活性化関数、しきい値処理など、配列上の要素ごとの数学関数を SIMD バージョンに変更します。
- 実行する価値がある理由: スピーチの中で
SIMD8<Float>この例では、3 セグメント関数をマスク計算に変更しており、オンサイトの結果はほぼ 3 倍高速になっています。 - 開始方法: 最もホットなスカラー ループから開始し、入力を押します。
SIMD8<Float>小分けにして使用しますreplacing(with:where:)条件付きロジックを表現し、配列の末尾要素を処理します。
5.Documents App のバックアップを暗号化する
- 何をすべきか: ユーザーのドキュメント ディレクトリを次の場所にエクスポートします。
.aeaファイルを保存し、復元時にキーを要求します。 - 価値がある理由: AEA は、ディレクトリのバックアップに適した圧縮、認証された暗号化、およびオプションの署名をストリーミング コンテナーに組み込みます。
- 開始方法: 作成する
ArchiveEncryptionContext、使用ArchiveByteStream.fileStream、ArchiveByteStream.encryptionStreamそしてArchiveStream.encodeStream3 層のフローをつなぎ合わせて呼び出します。writeDirectoryContents(archiveFrom:keySet:)。
関連セッション
- Metal Performance Shaders Graph で機械学習を加速する — BNNS の CPU パスを補完する、GPU 側の機械学習計算グラフを学習します。
- Core ML モデルを調整する — Core ML モデルがどのようにパフォーマンスのボトルネックを特定し、最適化するかを引き続き確認します。
- Create ML Framework を使用して動的 iOS アプリを構築する — デバイス側モデルのトレーニング プロセスを理解します。これは、BNNS の新しいトレーニング機能と併せて理解できます。
- SoundAnalysis に組み込まれたサウンド分類を確認する — Apple の機械学習フレームワークがオーディオ分類シナリオの上位レベルでどのように使用されるかを学びます。
コメント
GitHub Issues · utterances