스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
CPU에서 실시간 ML 추론 지원하기
BNNSGraph를 사용하여 CPU에서 머신 러닝 모델의 실행을 가속하는 방법을 살펴보세요. BNNSGraph를 통해 CPU에서 머신 러닝 모델을 컴파일 및 실행하는 방법과 오디오 또는 신호 처리 모델에 런타임 메모리를 할당하지 않고 단일 스레드로 실행하는 등 실시간 지원을 제공하는 방법을 알아봅니다.
챕터
- 0:00 - Introduction
- 1:18 - Introducing BNNS Graph
- 6:47 - Real-time processing
- 8:00 - Adopting BNNS Graph
- 14:52 - BNNS Graph in Swift
리소스
관련 비디오
WWDC24
-
다운로드
안녕하세요, 저는 Simon Gladman입니다 Apple의 Vector & Numerics Group에서 일하고 있습니다 오늘은 머신 러닝 라이브러리의 흥미로운 새 기능을 소개하겠습니다 Basic Neural Network Subroutine 즉 BNNS입니다
지난 몇 년 동안 BNNS는 CPU의 머신 러닝 추론과 훈련을 위한 포괄적인 API를 제공했습니다
현재 Apple은 BNNS가 더 빠르고 에너지 효율적이며 작업이 쉬워지도록 만들고 있습니다 이번 비디오에서는 CPU 기반 머신 러닝을 위한 훌륭한 차세대 API BNNS Graph를 소개합니다
먼저 새로운 API 소개로 시작하고 머신 러닝과 AI 모델을 최적화할 수 있는 방법을 설명하겠습니다 BNNS가 실시간 사용 사례의 요구 사항을 충족하게 만들 수 있는 중요한 기능을 살펴보겠습니다 그 다음으로 앱에 BNNS Graph를 도입하는 단계를 설명하겠습니다 마지막으로 BNNS Graph를 Swift에서 구현하는 방법과 SwiftUI 및 Swift Charts를 함께 사용하는 법도 살펴보겠습니다 차 한 잔 하시면서 좋아하는 의자에 앉아 함께 BNNS Graph를 살펴보죠
시작하기 전에, BNNS는 Apple의 전반적인 머신 러닝 프레임워크 스택에 원활하게 융화됩니다
BNNS는 Accelerate 프레임워크의 라이브러리 중 하나입니다 모델을 앱에 통합할 수 있게 해주죠
BNNS는 CPU에서 머신 러닝을 가속하고 Apple의 머신 러닝 프레임워크 Core ML에서 사용됩니다
BNNS Graph는 CPU 기반 머신 러닝을 위한 새로운 API로 BNNS 라이브러리가 개별 머신 러닝 프리미티브가 아닌 전체 그래프를 사용하게 해줍니다
훈련은 Apple 플랫폼에 모델을 배포하기 위한 첫 단계입니다 모델을 훈련시키고 나면 기기에 배포할 수 있도록 최적화 및 변환해야 합니다 이렇게 준비된 모델은 애플리케이션에 통합할 수 있습니다 이 영상에서는 작업 흐름 중 통합에 집중하겠습니다
BNNS Graph를 이해하기 위해 먼저 기존의 BNNS API를 살펴보죠
지금까지 BNNS는 레이어 중심의 API 세트를 제공했으며 머신 러닝에 사용할 수 있는 구성 요소인 개별 성능 프리미티브를 제공해 왔습니다 컨볼루션과 같은 각 단일 연산 작업에 많은 세부 사항을 구성해야 했죠
n차원 배열 설명자를 사용하여 레이어에 인자와 속성을 지정해야 했습니다 즉, 설명자를 일일이 출력과 입력 그리고 컨볼루션 커널 또는 가중치 행렬을 생성해야 했죠 그리고 짐작하시겠지만 BNNS는 컨볼루션 바이어스 기댓값을 개별 배열 설명자로 두었습니다
이러한 배열 설명자로 매개변수 구조를 만들고 레이어 자체를 생성하는 함수에 해당 매개변수 구조를 전달합니다 마지막으로 추론 중에 레이어를 적용합니다
BNNS를 기존 모델 구현에 사용하려면 각 레이어를 BNNS 프리미티브로 코딩하고 모든 중간 텐서에 코드를 작성해야 했죠
Apple은 지난 몇 년간 새로운 API인 BNNS Graph를 개발해 왔습니다 BNNS Graph는 여러 레이어 및 레이어 간 데이터 흐름으로 구성된 전체 그래프를 가지고 단일 그래프 객체로 사용합니다 개별 레이어 코드를 작성하지 않아도 되는 것이죠 게다가 개발자와 사용자는 더 우수한 속도 및 에너지 효율의 이점을 얻습니다 앱에 BNNS Graph를 통합하는 작업 흐름을 요약해 보겠습니다
먼저 Core ML 모델 패키지 mlpackage 파일로 시작합니다 mlpackage를 만드는 방법은 ‘Core ML을 사용하여 머신 러닝 및 AI 모델을 온디바이스로 배포하기’를 참고하세요
Xcode는 패키지를 mlmodelc 파일로 자동 컴파일합니다 이후 mlmodelc 파일에서 그래프 생성 코드를 작성해야 합니다 마지막으로 컨텍스트를 생성하여 그래프를 래핑하면 해당 컨텍스트가 추론을 수행합니다 BNNS Graph는 전체 모델을 인식하기 때문에 이전에는 불가능했던 여러 가지 최적화 작업이 가능해집니다 게다가 비용도 없죠
다음 예시는 모델의 일부 섹션입니다 이 섹션의 첫 번째 레이어는 텐서 A와 B의 요소별 덧셈을 수행하고 결과를 C에 작성합니다 다음 레이어는 컨볼루션을 수행합니다 그리고 모델은 컨볼루션 결과에 활성화 함수를 적용하고 마지막으로 슬라이스 레이어는 요소의 하위 집합을 텐서 D에 작성합니다
BNNS Graph의 최적화 작업에는 수학적 변환이 포함됩니다 이 예제에서는 슬라이스 연산이 마지막 레이어입니다 즉, 앞의 모든 연산이 슬라이스뿐만 아니라 전체 텐서에 적용됩니다
수학적 변환 최적화로 슬라이스가 모델의 시작 지점으로 이동하므로 BNNS는 해당 슬라이스의 요소 하위 집합만 계산하면 됩니다
레이어를 융합하여 최적화하면 일부 레이어를 단일 작업으로 결합하게 됩니다 이 예제에서는 BNNS가 컨볼루션과 활성화 레이어를 융합했습니다 또 다른 최적화는 ‘복사 생략’으로 슬라이스 연산이 슬라이스 내 데이터를 새로운 텐서로 단순 복사하는 것입니다
BNNS Graph는 슬라이스가 원본 데이터 윈도우를 통과하도록 최적화합니다
가능한 경우 텐서가 메모리를 공유하도록 함으로써 BNNS Graph는 메모리 사용량도 최적화하고 불필요한 할당을 제거합니다 이 예제에서 텐서 A, C는 같은 메모리를 공유할 수 있으며 텐서 B, D도 같은 메모리를 공유할 수 있습니다
BNNS Graph의 가중치 재조정 최적화는, 예를 들어 행 우선 레이아웃에서 블록 반복 순서로 가중치를 재조정하여 캐시 로컬리티를 개선할 수 있습니다
코드를 전혀 작성하지 않아도 이러한 최적화 기능이 자동으로 적용됩니다 또한 이전 BNNS 프리미티브보다 최소 2배 더 빠른 성능을 발휘할 수 있습니다
새로운 API는 실시간 사용 사례에 적합한데요 사용 사례를 하나 보겠습니다 AudioUnit에 BNNS Graph를 추가하는 오디오 사용 사례입니다
Audio Unit으로 iOS, macOS 앱에서 오디오 및 MIDI 데이터를 생성하고 수정할 수 있습니다 Logic Pro, GarageBand 같은 음악 제작 앱도 마찬가지죠 머신 러닝을 사용하는 Audio Unit은 다양한 기능을 제공합니다 목소리 분리 또는 제거를 위한 오디오 분리하기 콘텐츠에 따라 오디오를 다양한 리전으로 분할하기 다른 악기로의 음색 변환 등입니다
이 데모에서는 간단하게 Bitcrush 효과, 즉 오디오를 양자화하여 왜곡된 효과를 내는 Audio Unit을 만들어 보겠습니다
실시간 처리를 위해서는 실행 단계에서 메모리 할당이나 멀티스레딩을 피하는 것이 기본 조건인데요 커널 코드로 컨텍스트 전환이 발생하여 실시간 데드라인이 좌절될 수 있기 때문입니다 BNNS Graph를 사용하면 컴파일 및 모델 실행을 세밀하게 제어할 수 있으며 메모리 할당, 실행의 단일 또는 다중 스레드 여부 등 작업을 관리할 수 있습니다
이제 BNNS Graph를 채택한 Audio Unit 프로젝트를 만드는 방법을 알아보죠
Xcode의 Audio Unit 확장 프로그램 앱 템플릿을 사용하면 Audio Unit을 쉽게 만들 수 있습니다
먼저 bitcrusher mlpackage 파일을 프로젝트 탐색기로 드래그 앤 드롭합니다
Xcode는 mlpackage를 BNNS Graph 인스턴스화에 사용할 mlmodelc 파일로 컴파일합니다 이렇게 첫 2단계가 완료되었습니다
이제 Xcode에서 mlmodelc 파일을 사용할 수 있으므로 BNNS Graph 객체를 생성하고 수정 가능한 컨텍스트로 래핑할 준비가 되었습니다 그래프는 한 번만 빌드하므로 일반적으로 앱이 시작된 후 또는 사용자가 머신 러닝에 의존하는 기능을 처음 사용할 때 백그라운드 스레드에서 이 작업을 수행합니다
그래프 컴파일은 컴파일된 Core ML 모델을 최적화된 BNNS Graph 객체로 처리합니다 이 객체에는 추론이 호출하는 커널 목록과 중간 텐서에 대한 메모리 레이아웃 맵이 포함됩니다
Xcode 템플릿은 비즈니스 로직과 사용자 인터페이스에는 Swift와 SwiftUI를 실시간 처리에는 C++를 사용합니다
C++ DSP 커널 헤더 파일은 이 프로젝트에서 모든 신호 처리가 발생하는 곳입니다 그 위치에 BNNS Graph 코드를 추가할 것입니다
이를 위해 mlmodelc의 경로를 가져오고 BNNS Graph를 빌드하며 컨텍스트를 생성하고 인수 유형을 설정하고 작업 공간을 만들 것입니다 이제 코드 작성 방법을 살펴볼까요?
mlmodelc 파일의 경로를 가져오는 코드는 다음과 같습니다 프로젝트에 mlpackage 파일만 복사했고 Xcode가 패키지에서 mlmodelc 파일을 생성했다는 것을 떠올려 보세요
그래프가 실행 중에 단일 스레드만 사용하도록 지정하기 위해 기본 설정을 사용하여 컴파일 옵션 구조를 만들어 보겠습니다 기본 동작은 BNNS Graph가 다중 스레드에서 실행되는 것입니다 SetTargetSingleThread 함수를 호출하여 이 값을 변경해 보겠습니다
이제 컴파일 옵션과 mlmodelc 파일의 경로를 사용하여 그래프를 컴파일하겠습니다 GraphCompileFromFile 함수는 그래프를 생성합니다 두 번째 인수로 NULL을 전달하여 소스 모델의 모든 함수를 컴파일하도록 지정했습니다
컴파일이 완료되면 컴파일 옵션을 안전하게 할당 해제할 수 있습니다
이제 3단계가 완료되었습니다 그래프가 만들어졌습니다 이제 변경할 수 없는 그래프가 만들어졌으니 ContextMake 함수를 사용해 그래프를 수정 가능한 컨텍스트로 래핑하겠습니다 동적 형태와 특정 실행 옵션을 지원하려면 BNNS Graph는 수정 가능한 컨텍스트가 필요합니다 컨텍스트를 통해 콜백 함수를 설정하는 것도 가능하기 때문에 출력과 작업 공간 메모리를 직접 관리할 수 있습니다
BNNS Graph는 형태, 스트라이드와 순위를 지정하고 기본 데이터를 가리키는 텐서 구조를 사용하거나 기본 데이터에 대한 포인터로 직접 작동할 수도 있습니다
이 데모에서는 오디오 버퍼와 직접 작동하므로 컨텍스트의 인수가 포인터라고 지정하겠습니다
BNNS가 오디오 데이터를 처리하는 동안 메모리를 할당하지 않도록 해야겠죠 그래서 초기화 중에 페이지 정렬 작업 공간을 생성하겠습니다
작업할 데이터의 최대 크기를 알 수 있도록 컨텍스트를 업데이트해야 합니다 이를 위해 Audio Unit이 렌더링할 수 있는 최대 프레임을 기반으로 한 형태를 SetDynamicShapes 함수에 전달하겠습니다
이제 GetWorkspaceSize 함수는 작업 공간에 할당해야 하는 메모리의 양을 반환합니다
작업 공간 메모리는 페이지 정렬이 되어 있어야 하며 aligned_alloc을 호출하여 작업 공간을 생성합니다
중요한 점은 원래의 Python 코드의 인자 순서가 mlmodelc 파일의 인자 순서와 다를 수 있다는 것입니다 GraphGetArgumentPosition 함수는 각 인수의 올바른 위치를 반환하며 이 위치는 잠시 후에 실행 함수에 전달할 것입니다
이제 컨텍스트가 올바르게 구성되었습니다
계속 진행하기 전에 몇 가지 다른 옵션을 간단히 언급하고 싶습니다 컴파일 옵션으로 작업하는 동안 최적화 기본 설정을 할 수 있는 기회가 있었습니다 기본적으로 BNNS는 Audio Unit에 적합한 성능으로 그래프를 최적화합니다
성능을 최적화한다는 것은 BNNS Graph 객체의 공간이 증가하더라도 추가 작업이 컴파일 단계로 이동할 수 있다는 것을 의미합니다
하지만 여러분 앱의 공간이 중요한 경우 크기에 맞게 최적화할 수 있습니다 크기에 맞게 최적화하면 데이터가 가능한 가장 작은 형태로 유지되지만 실행 성능이 떨어질 수 있습니다 변환 수행 비용 때문이죠
또 다른 팁은 BNNS Graph에 NaNAndInfinityChecks를 활성화하는 함수가 포함되어 있다는 것입니다 이 디버깅 설정은 16비트 누산기가 오버플로될 때 텐서에서 무한대와 같은 문제를 감지하는 데 도움이 됩니다 하지만 이 검사를 실제 프로덕션 코드에서는 활성화하지 않는 것이 좋습니다
드디어 완성된 그래프와 컨텍스트를 초기화하고 단일 스레드 실행을 지정하고 작업 공간을 확보하여 BNNS가 할당을 수행하지 않도록 했습니다 그래프를 실행할 준비가 되었습니다 이제 이를 실행하는 데 필요한 코드를 살펴보겠습니다
SetBatchSize 함수는 입력 및 출력 신호 형태의 첫 번째 차원 크기를 프레임의 오디오 샘플 수로 설정합니다 이 경우 두 번째 인수는 소스 파일의 함수 이름을 나타냅니다 하지만 소스 파일에는 하나의 함수만 포함되어 있으므로 NULL을 전달할 수 있습니다
다섯 개의 인수 출력 및 입력 신호 그리고 양자화 양을 정의하는 스칼라 값을 실행 함수에 배열로 전달하겠습니다 가장 먼저 추가할 인수는 출력 신호입니다 저는 현재 오디오 채널의 outputBuffer를 기반으로 data_ptr 및 data_ptr_size 필드를 지정하겠습니다
다음 인수는 입력 신호입니다 현재 오디오 채널의 inputBuffer를 기준으로 data_ptr 및 data_ptr_size 필드를 지정하겠습니다
그 다음 인수들은 사용자 인터페이스의 슬라이더에서 파생된 세 개의 스칼라 값입니다
이제 함수를 실행할 수 있습니다 GraphContextExecute 함수는 컨텍스트와 인수 그리고 중요한 작업 공간까지 받아들입니다 반환 시 출력 포인터에는 추론 결과를 포함됩니다 SetBatchSize 함수와 마찬가지로 두 번째 인수는 소스 파일의 함수 이름을 참조하며 소스 파일에는 하나의 함수만 포함되어 있으므로 NULL을 전달합니다
마지막으로 Swift 프로젝트에서의 BNNS Graph 통합 방법을 살펴보죠
Swift를 사용하는 완벽한 사용 사례 중 하나는 Audio Unit의 SwiftUI 컴포넌트에 BNNS Graph를 구현하는 것입니다 이렇게 하면 Audio Unit의 사용자 인터페이스가 오디오 신호 자체와 동일한 파라미터로 동일한 모델에서 처리된 사인파를 표시할 수 있습니다 Audio Unit의 사용자 인터페이스 컴포넌트는 Swift UI를 사용하며 sampleCount 요소를 포함하고 부드러운 사인파를 나타내는 데이터에 bitcrusher 모델을 적용합니다
srcChartData 버퍼는 사인파 표현을 저장합니다 그리고 dstChartData 버퍼는 BNNS Graph가 효과를 적용한 후의 사인파 데이터를 저장합니다
이 세 버퍼는 사용자가 사용자 인터페이스에서 슬라이더로 제어하는 스칼라 값을 저장합니다
API는 C와 Swift 간에 일관성이 있으므로 앞서 살펴본 오디오 처리 코드와 마찬가지로 그래프와 컨텍스트를 정의하겠습니다
Swift는 C++에서처럼 실시간 안전을 보장하는 작업 공간을 제공하지 않지만 BNNS Graph는 실행 중 메모리를 할당할 필요가 없기 때문에
Audio Unit의 성능과 에너지 효율을 개선하는 데 도움이 될 것입니다
다음으로 인수 배열 내에서 인수의 위치를 정의하는 인수 인덱스 변수를 선언하겠습니다
여기서 보시는 것은 파형 표시 컴포넌트에 대한 초기화 메서드의 코드입니다 그리고 여기에서 그래프, 컨텍스트 작업 공간을 생성합니다
첫 번째 단계는 Xcode가 mlpackage에서 컴파일한 mlmodelc 파일의 경로를 가져오는 것입니다
다음으로 mlmodelc를 BNNS Graph 객체로 컴파일합니다
그 다음에는 C++ 코드에서 했던 것처럼 그래프 컨텍스트를 생성합니다
사용자 인터페이스와 오디오 처리 모두에 동일한 컨텍스트를 사용하지 않는 이유가 궁금하실 텐데요 컨텍스트가 한 번에 한 스레드에서만 실행될 수 있기 때문입니다 Audio Unit이 데이터를 처리하는 동안 사용자가 슬라이더 중 하나를 조정할 수 있으므로 프로젝트의 각 부분에 대해 별도의 컨텍스트가 필요합니다
빠르게 확인해 보죠 BNNS가 그래프와 컨텍스트를 성공적으로 생성했습니다
오디오 처리 코드에서 했던 것처럼 소스 및 대상 차트 데이터에 대한 포인터로 직접 작업하겠습니다 인수가 텐서가 아닌 포인터라는 것을 컨텍스트에 알립니다
이 경우 소스 및 대상 신호 데이터 버퍼의 첫 번째 차원 크기인 배치 크기는 예제 사인파의 샘플 수입니다
SetBatchSize 함수가 반환된 후 GetWorkspaceSize 함수는 샘플 수에 맞는 올바른 크기를 반환합니다
인수 배열로 인덱스를 계산하겠습니다 그리고 사용자가 슬라이더 값을 변경할 때마다 Swift는 사인파에 bitcrusher 효과를 적용하는 updateChartData() 함수를 호출합니다
updateChartData() 함수의 첫 번째 단계는 스칼라 값을 해당 스토리지에 복사하는 것입니다
그런 다음, 인덱스를 사용하여 인수 배열을 올바른 순서로 생성합니다
이제 샘플 사인파에서 bitcrusher를 실행할 수 있습니다 실행 함수가 반환되면 SwiftUI가 사용자 인터페이스의 차트를 업데이트해 bitcrush된 사인파를 표시합니다
파형을 표시하는 Swift 차트를 이미 추가한 Xcode로 돌아가 보겠습니다 인수를 저장하는 버퍼를 선언하겠습니다
그리고 그래프, 컨텍스트 인수 인덱스를 선언합니다
이니셜라이저 내부에서 그래프와 컨텍스트를 초기화하고
작업 공간을 생성하고 인수의 인덱스를 계산한 다음 updateChartData() 함수에서 인덱스를 사용하여 올바른 순서를 보장하는 인자 함수를 만들고
그래프를 실행합니다
이제 Xcode 실행 버튼을 눌러서 앱을 시작하고 bitcrusher의 작동을 들어보겠습니다
시간을 내 주셔서 감사합니다 요약으로 마무리해 보겠습니다 BNNS Graph는 에너지 효율적인 고성능 실시간 및 지연 시간에 민감한 머신 러닝을 CPU에 제공할 수 있는 API를 제공합니다 오디오 앱에 아주 알맞죠 다시 한 번 감사드립니다
-
-
0:01 - Create the graph
// Get the path to the mlmodelc. NSBundle *main = [NSBundle mainBundle]; NSString *mlmodelc_path = [main pathForResource:@"bitcrusher" ofType:@"mlmodelc"]; // Specify single-threaded execution. bnns_graph_compile_options_t options = BNNSGraphCompileOptionsMakeDefault(); BNNSGraphCompileOptionsSetTargetSingleThread(options, true); // Compile the BNNSGraph. bnns_graph_t graph = BNNSGraphCompileFromFile(mlmodelc_path.UTF8String, NULL, options); assert(graph.data); BNNSGraphCompileOptionsDestroy(options);
-
0:02 - Create context and workspace
// Create the context. context = BNNSGraphContextMake(graph); assert(context.data); // Set the argument type. BNNSGraphContextSetArgumentType(context, BNNSGraphArgumentTypePointer); // Specify the dynamic shape. uint64_t shape[] = {mMaxFramesToRender, 1, 1}; bnns_graph_shape_t shapes[] = { (bnns_graph_shape_t) {.rank = 3, .shape = shape}, (bnns_graph_shape_t) {.rank = 3, .shape = shape} }; BNNSGraphContextSetDynamicShapes(context, NULL, 2, shapes); // Create the workspace. workspace_size = BNNSGraphContextGetWorkspaceSize(context, NULL) + NSPageSize(); workspace = (char *)aligned_alloc(NSPageSize(), workspace_size);
-
0:03 - Calculate indices
// Calculate indices into the arguments array. dst_index = BNNSGraphGetArgumentPosition(graph, NULL, "dst"); src_index = BNNSGraphGetArgumentPosition(graph, NULL, "src"); resolution_index = BNNSGraphGetArgumentPosition(graph, NULL, "resolution"); saturationGain_index = BNNSGraphGetArgumentPosition(graph, NULL, "saturationGain"); dryWet_index = BNNSGraphGetArgumentPosition(graph, NULL, "dryWet");
-
0:04 - Execute graph
// Set the size of the first dimension. BNNSGraphContextSetBatchSize(context, NULL, frameCount); // Specify the direct pointer to the output buffer. arguments[dst_index] = { .data_ptr = outputBuffers[channel], .data_ptr_size = frameCount * sizeof(outputBuffers[channel][0]) }; // Specify the direct pointer to the input buffer. arguments[src_index] = { .data_ptr = (float *)inputBuffers[channel], .data_ptr_size = frameCount * sizeof(inputBuffers[channel][0]) }; // Specify the direct pointer to the resolution scalar parameter. arguments[resolution_index] = { .data_ptr = &mResolution, .data_ptr_size = sizeof(float) }; // Specify the direct pointer to the saturation gain scalar parameter. arguments[saturationGain_index] = { .data_ptr = &mSaturationGain, .data_ptr_size = sizeof(float) }; // Specify the direct pointer to the mix scalar parameter. arguments[dryWet_index] = { .data_ptr = &mMix, .data_ptr_size = sizeof(float) }; // Execute the function. BNNSGraphContextExecute(context, NULL, 5, arguments, workspace_size, workspace);
-
0:05 - Declare buffers
// Create source buffer that represents a pure sine wave. let srcChartData: UnsafeMutableBufferPointer<Float> = { let buffer = UnsafeMutableBufferPointer<Float>.allocate(capacity: sampleCount) for i in 0 ..< sampleCount { buffer[i] = sin(Float(i) / ( Float(sampleCount) / .pi) * 4) } return buffer }() // Create destination buffer. let dstChartData = UnsafeMutableBufferPointer<Float>.allocate(capacity: sampleCount) // Create scalar parameter buffer for resolution. let resolutionValue = UnsafeMutableBufferPointer<Float>.allocate(capacity: 1) // Create scalar parameter buffer for resolution. let saturationGainValue = UnsafeMutableBufferPointer<Float>.allocate(capacity: 1) // Create scalar parameter buffer for resolution. let mixValue = UnsafeMutableBufferPointer<Float>.allocate(capacity: 1)
-
0:06 - Declare indices
// Declare BNNSGraph objects. let graph: bnns_graph_t let context: bnns_graph_context_t // Declare workspace. let workspace: UnsafeMutableRawBufferPointer // Create the indices into the arguments array. let dstIndex: Int let srcIndex: Int let resolutionIndex: Int let saturationGainIndex: Int let dryWetIndex: Int
-
0:07 - Create graph and context
// Get the path to the mlmodelc. guard let fileName = Bundle.main.url( forResource: "bitcrusher", withExtension: "mlmodelc")?.path() else { fatalError("Unable to load model.") } // Compile the BNNSGraph. graph = BNNSGraphCompileFromFile(fileName, nil, BNNSGraphCompileOptionsMakeDefault()) // Create the context. context = BNNSGraphContextMake(graph) // Verify graph and context. guard graph.data != nil && context.data != nil else { fatalError()}
-
0:08 - Finish initialization
// Set the argument type. BNNSGraphContextSetArgumentType(context, BNNSGraphArgumentTypePointer) // Set the size of the first dimension. BNNSGraphContextSetBatchSize(context, nil, UInt64(sampleCount)) // Create the workspace. workspace = .allocate(byteCount: BNNSGraphContextGetWorkspaceSize(context, nil), alignment: NSPageSize()) // Calculate indices into the arguments array. dstIndex = BNNSGraphGetArgumentPosition(graph, nil, "dst") srcIndex = BNNSGraphGetArgumentPosition(graph, nil, "src") resolutionIndex = BNNSGraphGetArgumentPosition(graph, nil, "resolution") saturationGainIndex = BNNSGraphGetArgumentPosition(graph, nil, "saturationGain") dryWetIndex = BNNSGraphGetArgumentPosition(graph, nil, "dryWet")
-
0:09 - Create arguments array
// Copy slider values to scalar parameter buffers. resolutionValue.initialize(repeating: resolution.value) saturationGainValue.initialize(repeating: saturationGain.value) mixValue.initialize(repeating: mix.value) // Specify output and input arguments. var arguments = [(dstChartData, dstIndex), (srcChartData, srcIndex), (resolutionValue, resolutionIndex), (saturationGainValue, saturationGainIndex), (mixValue, dryWetIndex)] .sorted { a, b in a.1 < b.1 } .map { var argument = bnns_graph_argument_t() argument.data_ptr = UnsafeMutableRawPointer(mutating: $0.0.baseAddress!) argument.data_ptr_size = $0.0.count * MemoryLayout<Float>.stride return argument }
-
0:10 - Execute graph
// Execute the function. BNNSGraphContextExecute(context, nil, arguments.count, &arguments,
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.