whisper.cpp는 Apple Silicon용 선택적 Core ML 인코더 경로를 제공합니다. Mac 받아쓰기 앱이라면 당연히 켤 것 같은 기능입니다. Voice Type은 이를 강제로 비활성화하고 Metal 백엔드를 대신 실행합니다.
이유는 형태(shape) 불일치이며, 실시간 받아쓰기가 오디오를 배치로 묶는 방식에서 비롯됩니다.
TL;DR
- 컴파일된 Core ML 인코더 번들은 고정된 입력 형태를 기대합니다.
- 실시간 받아쓰기는 길이가 제각각인 배치를 만듭니다. 배칭 로직이 구절을 중간에서 자르지 않기 때문입니다.
- 그래서 Voice Type은 컨텍스트를 만들 때 Core ML 경로를 비활성화하고, 가변 길이 오디오를 받아들이는 Metal로 실행합니다.
- 이 경로를 없애면서 모델당 163 MB~1.17 GB의 다운로드도 함께 사라졌습니다.
배치 길이가 제각각인 이유
Voice Type은 고정된 타이머 간격으로 자른 오디오를 인식기에 보내지 않습니다. 스트리밍 음성 활동 감지기가 발화 구간을 표시하고, 배칭 로직은 발화 구간 전체를 누적합니다. 타이머가 울렸다는 이유로 구간 하나를 쪼개지 않습니다.
배치를 모델에 보낼 시점을 정하는 규칙은 모두 시계가 아니라 발화에 따라 달라집니다.
- 실시간 배치의 목표는 29초이며, 15~29초 범위로 제한됩니다.
- 5초보다 짧은 배치는 일시 정지(휴지)만으로는 절대 내보내지 않습니다. 기다렸다가 다음 구절이 합쳐집니다.
- 일시 정지에 따른 플러시에는 닫힌 발화 구간 뒤에 최소 400 ms의 무음이 필요합니다.
- 다음 구간 전체를 추가하면 목표를 넘게 되는 경우, 현재 배치를 먼저 내보내고 그 구간 전체가 다음 배치를 시작합니다.
- 단축키를 놓으면, 남아 있는 것은 길이와 상관없이 모두 내보냅니다.
그 결과 배치 길이는 상수가 아니라 분포가 됩니다. 배치는 5초일 수도, 31초일 수도 있습니다. 받아쓰기에는 이것이 올바른 동작입니다. 그렇지 않으면 윈도 경계에서 단어를 반으로 잘라 버리게 되기 때문입니다.
Core ML에서 치르게 되는 비용
Core ML 모델은 입력 차원이 고정된 채로 .mlmodelc 번들로 미리 컴파일됩니다. 컴파일 때와 다른 길이의 오디오를 넣으면, 고정된 형태에 맞추어 패딩하거나(무음에 대해서도 인코더 연산 비용을 치르게 됩니다), 컴파일된 변형을 여러 개 보관해야 합니다(디스크에 번들이 여러 개 생깁니다).
Voice Type은 둘 중 어느 쪽도 택하지 않습니다. Core ML 인코더 경로는 whisper 컨텍스트가 만들어지는 곳에서 강제로 비활성화되어 있으며, 주석에도 바로 이 이유, 즉 고정 형태 입력 제약이 명시되어 있습니다. 그다음 백엔드 정책은 기본적으로 Metal을 선택하고, 시뮬레이터에서, 사용자 기본 설정이 GPU를 끈 경우에, 또는 크래시 이후 안전 모드가 고정된 경우에만 CPU로 대체합니다.
Metal은 배칭 로직이 만들어 내는 어떤 길이든 받아들입니다.
제거로 확보한 것
Core ML 지원은 단순히 비활성화된 것이 아니라 제거되었습니다. 이전 빌드에서는 각 모델의 가중치 옆에 인코더 번들 ggml-<name>-encoder.mlmodelc를 설치했고, 모델에 따라 대략 163 MB에서 1.17 GB에 달했습니다. 이 번들들은 모델마다 다운로드, 압축 해제, 배치되었습니다.
더 이상 이를 삭제하는 코드 경로가 없기 때문에, 앱은 업그레이드 시 모델 디렉터리에서 고아가 된 인코더 번들을 정리하는 일회성 마이그레이션을 포함하며, 옆에 있는 .bin 가중치는 절대 건드리지 않도록 주의합니다.
따라서 Core ML을 끈 결과로 사용자가 체감하는 것은 더 작은 다운로드와 더 작은 모델 디렉터리이며, 거기에 문장이 얼마나 길었는지 신경 쓰지 않는 인식기가 더해집니다.
점선 분기는 앱이 의도적으로 택하지 않는 경로입니다. 출처: whisper.cpp.swift/LibWhisper.swift.
Metal이 실제로 실행하는 것
Metal은 Apple의 저수준 GPU API이며, 이 앱에서는 두 가지 별개의 작업을 맡고 있습니다.
whisper 컨텍스트는 Metal 백엔드에서 실행되므로 인코더와 디코더가 GPU에서 동작합니다. 이와 별도로, 배치별 오디오 조정이 모델이 무엇이든 받기 전에 독립된 신호 처리 단계로 실행됩니다. DC 오프셋 제거, 필터와 음량 제어, 그리고 모델이 기대하는 16 kHz로의 리샘플링입니다.
둘 다 통합 메모리에서 읽기 때문에, 캡처 경로와 모델 사이에서 오디오 버퍼가 버스를 거쳐 복사되지 않습니다.
주장하지 않는 것
여기서는 마무리(finalization) 시간을 공개하지 않습니다. 그 숫자와 함께 드릴 수 있는 벤치마크 스크립트가 현재 없고, 재현 방법이 없는 지연 시간 수치는 마케팅이기 때문입니다.
벤치마크 없이도 구조적으로 참인 것은, 단축키를 놓은 시점에 남은 작업은 말한 시간이 아니라 배치 목표에 의해 상한이 정해진다는 점입니다. 2분짜리 받아쓰기와 30분짜리 받아쓰기는 마무리해야 할 양이 같은 상태로 끝납니다.
직접 시간을 재 보려면 같은 문장을 열 번 받아쓰고, 단축키를 놓은 시점부터 텍스트가 입력되기까지의 간격을 재 보세요. 중요한 것은 그 숫자이고, 그 숫자는 여러분의 것입니다.
트레이드오프
모델 크기와 정확도의 트레이드오프가 있습니다. 작은 모델은 더 빨리 마무리되지만 놓치는 단어가 더 많습니다. Voice Type은 27 MB부터 550 MB까지 여러 크기를 제공하므로, 이 트레이드오프는 여러분이 정할 수 있습니다.
큰 모델은 통합 메모리에 여유가 있는 Apple Silicon Mac에 어울립니다. 작은 모델은 8 GB 기기에 어울립니다. 8 GB 기기에서는 큰 모델이 디코딩보다 페이징에 시간을 쓰기 때문입니다.
