본문으로 건너뛰기

© 2026 Molayo

Qiita헤드라인2026. 06. 10. 17:54

Apple의 새로운 AI 추론 프레임워크 Core AI를 위한 OSS를 가장 빠르게 구축했습니다

요약

Apple의 새로운 AI 추론 프레임워크인 Core AI를 위한 오픈소스 모델 변환 도구와 리포지토리를 소개합니다. Gemma 4, Qwen 3 등 최신 모델을 Core AI 형식으로 변환하는 방법과 PyTorch의 torch.export를 활용한 워크플로우를 다룹니다.

핵심 포인트

  • Gemma 4 및 Qwen 3 모델의 Core AI 형식 변환 지원
  • PyTorch의 torch.export 메커니즘을 활용한 변환 방식
  • AOT 컴파일을 통한 iOS 디바이스 로딩 속도 개선
  • iPhone에서 즉시 실행 가능한 샘플 채팅 앱 제공

Apple의 새로운 AI 프레임워크, Core AI가 공개되었습니다.

좋아하는 AI/LLM 모델을 Core AI 형식으로 변환하여 iOS나 macOS에서 사용할 수 있습니다.

Apple 공식 CoreAI-Models는 Gemma 3나 Qwen 3 등의 변환 방법을 지원합니다.

저는 Core AI의 형님 격인 Core ML 프레임워크 시절부터,

CoreML-Models라는 AI 모델 모음(iOS/macOS 대응)을 OSS로 만들어 왔고,

CoreML-LLM이라는 LLM을 Core ML에서 사용하는 리포지토리(Repository)도 만들었기에,

이번 Core AI 발표를 듣고

"좋아, 한번 해보자"라며 소매를 걷어붙이고,

아직 공식이 지원하지 않는 모델(Gemma 4나 Qwen 3 등)을 Core AI 형식으로 변환한 것을

공개했습니다.

변환된 모델, 변환 코드, Core AI 특유의 지식 문서가 정리되어 있습니다.

GitHub에 올려두었으니 자유롭게 활용해 주세요.

iPhone에서 즉시 빌드하여 사용할 수 있는 샘플 앱도 포함되어 있습니다.

단위: tok/s

macOS GPU (M4 Max)iOS GPU (iPhone 17 Pro)iOS ANE
Gemma 4 E2B57.022
Qwen3.5-0.8B58.528

출력은 오리지널과 동등합니다.

Core ML과의 실무적인 큰 차이점은 두 가지입니다. 변환의 입구가 torch.export라는 점(coremltools와 같은 트레이서(Tracer) 독자 구현이 아니라, PyTorch 표준의 익스포트(Export) 메커니즘임)과, 커스텀 Metal 커널이 정식 기능이라는 점입니다.

PyTorch 모델
│ torch.export(트레이스(Trace))
▼
...

.aimodel은 디바이스에 독립적입니다. 최초 로드 시 OS가 해당 디바이스에 맞춰 specialize(무거운 컴파일)를 수행하고, 결과는 콘텐츠 해시로 캐시합니다. 첫 번째만 기다리면 되고, 두 번째부터는 빠릅니다. LLM 사이즈의 경우 첫 번째는 수 초~수십 초가 걸립니다.

이를 앞서 처리하는 것이 AOT 컴파일입니다:

xcrun coreai-build compile model.aimodel --platform iOS --preferred-compute neural-engine
# → 아키텍처별 .aimodelc(컴파일된 아티팩트(Artifact))

실측 결과 Gemma 4의 1.9 GB 모놀리스(Monolith) 모델로 최초 로드 19.2초 → 4.9초(~4배). 단, tok/s는 변하지 않습니다. 또한 Mac은 원래 ~1초 만에 specialize하므로 AOT가 불필요하며, 실익은 디바이스 측에 있습니다. 아키텍처 지정에는 함정이 있는데, iPhone 17 Pro는 h17p가 아니라 h18p(마케팅명이 아닌 디바이스 식별자 iPhone18,1 유래)입니다. 가장 간단한 경로는 리포지토리의 채팅 앱입니다:

git clone https://github.com/john-rocky/coreai-model-zoo

  • apps/CoreAIChat/을 Xcode 27 beta로 빌드하여 iPhone(iOS 27 beta)으로 - 앱 내 다운로드로 Hugging Face에서 모델을 취득 → 채팅

CLI 파는 swift/CoreAIRunner(Swift 패키지)로 .aimodel을 직접 사용할 수 있습니다.

모델별 구성과 실행 절차는 각 HF 모델 카드와 zoo의 카드에 적혀 있습니다.

검증된 최소 플로우입니다(함정 코멘트 포함):

import torch, shutil
from pathlib import Path
from coreai_torch import TorchConverter, get_decomp_table
...

주의할 점: AIModel.load는 async, load_function은 sync, 함수 호출은 다시 async입니다. 미지원되는 ATen 연산은 실행 시점이 아니라 add_exported_program 검증 시에 나타납니다.

그리고 검증은 「cosine ≈ 1.0 + top-1 argmax 일치」로 판정하십시오. 은닉 상태(hidden state)의 maxdiff가 크게 보이는 것은 대부분 fp16 계산 노이즈이며, argmax가 일치한다면 실용적으로는 일치하는 것입니다.

자세한 내용은 knowledge/conversion-guide.md를 참조하세요.

저수준 (low-level) API의 골격은 다음과 같습니다:

import CoreAI
let prepared = try await PreparedModel.prepare(at: url) // 최초 실행 시 여기서 specialize가 실행됨
let fn = try prepared.model.loadFunction(named: "main")!
...

state 버퍼는 호출 간에 동일한 것을 재사용하는 것(in-place 업데이트를 통해 KV/SSM 상태를 유지)이 핵심입니다.

Apple의 고수준 (high-level) 파이프라인(CoreAILM)은 「input_ids → logits + KV 캐시 1세트」의 표준 구성만 다룰 수 있으므로, Qwen3.5(상태 4개)나 Gemma 4(듀얼 KV + per-layer embedding)와 같은 구성은 이 저수준 API 위에 얇은 러너(runner)를 작성해야 합니다. 그 범용 버전이 swift/CoreAIRunner입니다.

자세한 내용은 knowledge/swift-runtime.md를 참조하세요 (실기기로의 모델 전송 시 주의사항 — 중간에 복사된 파일을 로드하면 specialization 캐시가 오염됨 — 이 내용도 포함되어 있습니다).

GPU:

그래프는 MPSGraph로 로워링 (lowering)됩니다.

TorchMetalKernel로 작성한 MSL은 실제 그래프 연산(graph op)으로서 .aimodel에 내장되어, 모델과 함께 배포되어 OS 런타임 내에서 동작합니다 (AOT도 유지됩니다).

커스텀 커널은 GPU 전용입니다.

ANE:

고정된 하드웨어 연산(hardware op)만 동작하는 fp16 전용 유닛입니다.

fp32 정밀도가 필요한 계산은, 내부적으로 fp32로 누적하는 하드웨어 연산(LayerNorm, Conv2d)을 선택함으로써만 얻을 수 있습니다. 이것이 위에서 언급한 [x,−x] 트릭과 1×1 Conv2d를 사용하는 이유입니다.

동적 형상 (dynamic shape)은 느립니다:

형상이 바뀔 때마다 재 specialize가 실행됩니다.

LLM 디코딩은 고정 형상 버킷 (fixed shape bucket) (패딩을 통해 동일한 형상을 재사용)이 기본입니다.

・HuggingFace의 model 코드는 그대로는 torch.export를 통과하지 못하므로, 트레이스 가능(traceable)하고 고정 형상인 decode-step 함수로 다시 작성하고, HF의 가중치를 옮긴 뒤, cosine + top-1으로 레이어별로 대조합니다.

・Qwen3.5(Mamba 계열 gated-delta + 주기적 풀 어텐션, 하이브리드 SSM)의 while_loop 스캔은 실기기의 Swift 런타임에서 로워링(lower)할 수 없습니다. 디코딩 시에는 스캔 길이가 항상 1이므로, 루프가 없는 단일 단계 업데이트(bit 일치)로 교체하면 GPU와 ANE 모두에서 동작합니다. 덤으로, 하이브리드 모델은 컨텍스트를 8배(256→2048)로 늘려도 속도가 -9%밖에 느려지지 않습니다.

베타 버전의 KV 쓰기 크래시는 회피할 수 있습니다. 그래프 내 KV 캐시 쓰기(인덱스 형태)는 재현성 100%로 크래시가 발생합니다 (보고됨: FB23024751 / apple/coreai-models#5). 회피 방법은 호스트 관리 KV(Gemma 4를 13→27 tok/s로 끌어올린 구성)를 사용하는 것입니다. 나아가 one-hot 마스크를 입력으로 전달하는 「에스케이프(escape)」 방식을 통해 stateful한 그래프 내 KV도 복구할 수 있습니다.

・커스텀 Metal 커널로 13→57 tok/s를 달성했습니다. fused int8 dequant-LUT matvec(FFN) + 262,144 어휘 헤드의 in-kernel argmax를 사용했습니다. iPhone에서는 int4 k-means 버전이 1.43배의 이득을 얻어 22 tok/s의 출하 구성으로 이어졌습니다. 반면 Mac의 int4는 속도와 정밀도 모두 실패했습니다. 어떤 병목(대역폭 / ALU / 디스패치)에 부딪히고 있는지를 아는 것이 최적화의 정답을 결정합니다. 참고로 동일한 M4 Max에서 MLX는 ~160 tok/s를 기록합니다 — 이 격차는 커널 최적화만으로는 메울 수 없는 구조적인 문제입니다.

ANE는 fp16 상태로 완전 일치를 구현할 수 있습니다. ANE에서 출력이 깨지는 근본적인 원인은 두 가지입니다: RMSNorm의 mean(x²)에서 발생하는 fp16 오버플로(→ LayerNorm(concat([x, −x]))의 전반부가 RMSNorm이라는 항등식을 이용하여, fp32 누적을 지원하는 하드웨어 LayerNorm으로 계산을 이전함)와, fp16 matmul(행렬 곱셈) 누적의 복리 효과(→ 모든 nn.Linear를 1×1 Conv2d로 변경. ANE의 conv 엔진은 fp32로 MAC(Multiply-Accumulate) 연산을 수행함))입니다. 두 방법 모두 GPU와 ~2e-8 수준으로 일치합니다.

그래프는 state 텐서(호출 간에 지속되며, in-place로 업데이트됨)를 가질 수 있습니다.

LLM의 KV 캐시(KV Cache)에 딱 맞는 메커니즘이지만, 현재 베타 버전에서는 인덱스 형태의 그래프 내 쓰기 작업 시 크래시가 발생하므로(위의 FB23024751 참조), 현실적인 해결책은 host-cache(K/V를 일반적인 입출력으로 처리하고, 호스트 측에서 버퍼에 기록함)입니다.

상세 내용과 회피 방법은 knowledge/stateful-kv-cache.md 및 kvwrite-bug 페이지에 정리되어 있습니다.

🐣

프리랜서 엔지니어입니다.

AI에 관한 다양한 글을 쓰고 있으니, 괜찮으시다면 프로필을 확인해 주세요.

만약 다음과 같은 요청 사항이 있으시다면 언제든 편하게 상담해 주시기 바랍니다.

AI 서비스를 개발하고 싶다, 비즈니스에 AI를 도입하여 효율화하고 싶다, AI를 사용한 스마트폰 앱을 개발하고 싶다, AR을 사용한 애플리케이션을 만들고 싶다, 스마트폰 앱을 만들고 싶지만 어디에 상담해야 할지 모르겠다…

모두 중간 비용을 절감한 합리적인 가격으로 진행해 드릴 수 있습니다.

업무 상담은 이쪽으로 연락 주세요.

rockyshikoku@gmail.com

머신러닝(Machine Learning) 및 AR 기술을 사용한 애플리케이션을 만들고 있습니다.

머신러닝/AR 관련 정보를 발신하고 있습니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0