-
了解 Core AI
认识一下 Core AI,这是 Apple 新推出的设备端 AI 模型部署框架。一起浏览这个包罗万象的生态系统,看看如何借助 Python 资源库进行模型的转换、创建和优化,如何利用一种 Swift API 实现简单的即插即用推断,并构建高级用例来满足严格的延迟和内存要求等等。探索全新的 Core AI 模型存储库,其中包含适用于热门架构的现成示例。了解 Xcode 的深度集成 (包括模型的提前编译) 如何简化工作流程,从而帮助你交付更智能、更灵敏的 App 体验。
章节
- 0:00 - Introduction
- 0:33 - What is Core AI
- 4:57 - Model conversion
- 6:16 - App integration
- 10:48 - Profiling with Instruments
- 11:15 - Optimizing performance
- 14:13 - Additional features
- 15:34 - Specialization
- 20:07 - Next steps
资源
- Core AI PyTorch Extensions
- Core AI Python
- Core AI Optimization
- Core AI
- Compiling Core AI models ahead of time
- Managing model specialization and caching
相关视频
WWDC26
-
搜索此视频…
大家好 我是Ben 我是Core AI团队的工程师 今天我将介绍Core AI 并展示如何使用它 为你的App添加智能功能 AI的发展速度前所未有 此前看似遥不可及的 新模型和新能力 如今不断涌现 Core AI能帮你把握这一势头 在此基础上进行构建 Core AI标志着设备端 AI执行的下一次演进 遍及Apple各平台 它从底层为现代工作负载 而构建 并提供你所需的 高性能推理 以构建先进的AI功能 Core AI是驱动设备端 Apple 智能的推理框架 现在它可供你使用 将同等能力带给 你App自己的智能 Core AI不仅仅是一个框架 它是一套完整的技术 涵盖模型部署的全生命周期 从模型优化与转换 到调试与集成 至你的App 一切均为支持 快速迭代循环而设计 这正是构建优质AI功能所需 Core AI让你充分发挥 Apple Silicon的潜能 在CPU、GPU和神经网络引擎上 提供极速推理 该框架配备 现代Swift API 这是一个富有表现力的API 能满足你App对性能的需求 同时不牺牲内存安全性
更广泛的技术集 自然融入 常见的ML工程工作流 复用熟悉的Python 和PyTorch基础 用于模型编写、优化 和转换 Core AI还支持 广泛的自定义 从精细的推理管理 和模型专化 到自定义GPU内核 所有这些都紧密集成 到全新的开发者工具链 包括预编译、专用的 Core AI Instruments 以及强大的可视化调试器 可将张量值直接追溯 回原始Python源代码 Core AI旨在根据你的需求 和可用算力进行扩展 无论你是想让App识别 实时会议中的发言者 通过小型说话人分割模型 还是让用户将摄像头 对准任何物体并提问 通过更大的视觉语言模型 即时获得答案 或让他们将复杂的 多步骤任务 交给由700亿参数LLM 驱动的强大智能体助手处理 Core AI都能满足你的需求 所有这些都在Apple设备上 本地运行 无需服务器 无需按Token付费 在本次讲座中 我将首先展示 如何将模型转换为Core AI格式 然后介绍如何将 转换后的模型集成到App中 接着我将深入探讨 如何优化模型 和App的性能 最后我将重点介绍 Core AI的一些附加功能 及其相关工具 你可能会觉得有用 让我们开始吧 每一款优秀的App体验 都始于一个想法 也许你想构建一些 感觉有点神奇的东西 一些能智能响应的东西 或者能做出原本需要人工 或硬编码规则才能做出的决策 机器学习和AI 让这类体验成为可能 一旦你有了想法 下一步是寻找或构建 能够驱动它的模型 就像你的想法本身 会随时间演进一样 找到合适的模型 也是一个迭代过程 你会不断尝试 对照需求 进行评估并改进 Core AI旨在 支持这种迭代 使其尽可能快速 且顺畅 为了使这一点更加具体 我将实现一个有趣的游戏创意 这是一个让你玩 双人贪吃蛇游戏的App 其中一条蛇由通过 Core AI运行的AI模型驱动 该App将遵循 传统的贪吃蛇规则 蛇可以通过吃食物来成长 并且必须避免撞到墙壁 自身和另一条蛇 最后存活的蛇获胜 在每个时间步 AI模型 将看到一组特征 描述当前棋盘状态 这些特征将被累积 成为输入到模型的 完整游戏历史 然后预测 最佳移动方向 虽然贪吃蛇是一个简单的游戏 用于创建此体验的工具和API 是相同的基础 可以扩展到更大 更复杂的用例 我很好奇 对于这个项目 用PyTorch能做到什么 在AI编码助手 的一点帮助下 我很快就勾勒出了 一个简单的贪吃蛇动作预测模型 为了训练它 我使用了 朴素模拟来生成训练数据 只需运行游戏 并记录状态和动作 目的是从简单开始 让模型在我的App中运行 所以下一步 是获取这个PyTorch模型 并将其转换为Core AI 我将使用新的Core AI Torch Python包 轻松执行转换
首先我将加载SnakeTransformer 模块的已训练检查点 并准备一个示例输入 然后我将使用torch.export 导出torch程序 同时确保使用 dynamic_shapes参数 以指定特征的序列长度 是动态的 这样它就不会以 静态示例长度5进行追踪 同时我将对转换后的程序 运行分解 使用Core AI的分解表 接下来我将运行 Core AI的TorchConverter 指定输入和输出 的名称 最后将转换后的Core AI模型 保存到磁盘 在离开Python环境之前 我还要做一件事 运行测试以验证 转换后的Core AI模型 与我原始PyTorch模型 的数值一致 这可以通过Core AI框架 Python绑定轻松完成 首先我将加载 PyTorch和Core AI模型 然后准备一个贪吃蛇游戏 的示例输入 然后将相同的输入分别通过 PyTorch模块 和Core AI推理函数运行 最后对我的用例断言 一个足够小的差值 在PyTorch和Core AI输出之间 现在我有了转换后的AI模型 下一步是跳转到Xcode 并将模型集成到我的App中 首先我将用Xcode 打开AI模型文件 它会显示有关模型的信息 包括模型大小 操作的分布 以及其他有用的元数据 在Functions标签页中 它还显示模型中 每个唯一函数的确切函数签名 在本例中 模型 只有一个函数 它以游戏棋盘的特征 作为输入 并产生logits作为输出 指示模型认为 哪个方向最适合移动 还需注意NDArray值中 的问号 表示该维度 具有动态形状 这与我以动态序列长度 转换模型的方式一致 现在我已在Xcode项目中 包含了AI模型文件 并检查了其结构 下一步是使用 Core AI框架来运行模型 Core AI框架是 一个全新的Swift API接口 用于加载和运行Core AI模型 它提供了一组 逐步展开的API 使得快速启动和运行 变得简单 同时也具备 更深层的灵活性 以支持对性能 要求严苛的应用 此外它使用现代Swift语言特性 如不可逃逸类型 以提供内存安全的API 同时不牺牲性能 让我们先来讨论 框架中的核心类型 AIModel通过指向.aimodel文件 的URL进行初始化 主要用于检查和加载 一个或多个推理函数 InferenceFunction是 可运行的对象 代表单个已加载的 计算图 通常情况下 你的AIModel 只有一个主要的InferenceFunction 不过你可以将单个模型 转换为包含多个函数 AIModel和InferenceFunction 通常是对象 你将在准备App AI功能时构建它们 例如这可以在 App初始化时完成 NDArray是保存你的 多维输入和输出数据的类型 你在InferenceFunction上 使用run方法 来使用这些数据运行推理 最后你可以读取和处理 推理的输出 为了实现贪吃蛇游戏 我将首先创建ModelPlayer类型 在App初始化时 它将用指向AI模型文件 的URL进行初始化 然后它将初始化AIModel 并从中加载主推理函数 接下来是模型玩家 做决策的逻辑 它将遵循我在App中 定义的SnakePlayer协议 主要的协议要求 是chooseAction函数 它接收游戏历史记录 并返回蛇应该采取 的下一个动作 首先要做的是创建一个NDArray 来填充输入特征 对于这个推理函数 NDArray的预期结构 是二维的 包含float32数据 其中形状的第一个维度 是当前序列长度 第二个是固定的 隐藏维度大小 然后它将把特征 写入该NDArray 使用这个writeFeatures辅助函数 它接收游戏 和NDArray的可变视图 NDArray.MutableView类型 是一种不可逃逸类型 它提供对NDArray 底层存储的安全高效访问 准备好输入后 它将使用这些输入运行推理 并提取预期的 输出logits ndarray 最后一步是 对输出logits进行采样 以选择蛇下一步 移动的方向 通过将ndarray视图 传入辅助函数 它将读取这些值 并选择具有最大对应logit 的方向 writeFeatures函数 负责填充输入特征 让我简要介绍 这些特征包含什么
它们包含AI蛇头 到所有墙壁的归一化距离 到最近食物的 归一化相对X和Y距离
四个元素编码 当前方向 到另一条蛇的 归一化距离 最后是对手的方向 现在把这些整合在一起 我将进行一次测试运行 两条蛇都由AI模型驱动 看看效果如何
运行后可以看到 模型正在运行 但是我注意到游戏 随着进行变得越来越慢 除了Core AI框架之外 Xcode中还有一个新Instruments 帮助你分析App中 运行的Core AI模型 在本例中 我已使用 Instruments运行了App 我可以看到推理间隔 随时间明显增大 这意味着推理调用的 延迟在增加 这是合理的 因为Transformer模型 具有二次时间复杂度 相对于序列长度 在我们的游戏中 序列长度正在增加 随着模型的每次移动 在这种情况下 下一步是优化 模型使用的性能 每次输入序列增加时 Transformer模型都会重新计算 一组内部Key 和Value嵌入 用于序列中的每个元素 一种常用策略 用于提升性能 在使用Transformer的 这类解码循环中 是缓存Key和Value 这些Key和Value 是为序列中每个元素计算的 而不是在每次推理时 从头重新计算所有值 这可以通过Core AI 使用状态来实现
状态是模型的输入 在推理期间既可读取 也可原地更新 通过将Key和Value缓存 作为模型的状态引入 我们既避免在每次推理时 重新计算它们 也无需将完整的游戏历史 作为输入提供 因为来自旧步骤的所需数据 已存储在状态中 因此在第一次输入之后 每个后续步骤 都使用缓存作为历史记录 并且只获取 最新棋盘状态的新特征 为了实现Key/Value缓存 我将回到原始的 编写代码 并做一些修改 以添加Key和Value缓存 首先我将更新torch模块 添加Key和Value缓存张量 作为Transformer模块中的缓冲区 通过使用torch register_buffer API 这将使这些张量 成为导出的torch程序 中的可变缓冲区 Core AI将把它们转换为状态 然后在模块的forward函数中 我将添加逻辑 以实际使用这些缓存 这涉及从缓存中读取 之前的特征Key和Value 然后将新特征计算出的 Key和Value写回缓存 最后 我将重新运行 之前相同的代码来重新转换模型 但现在在convert调用中 添加state_names参数 以指定新状态参数 的名称 现在我已经用新的函数签名 重新转换了模型 我将更新App代码来处理它 首先 我将更新ModelPlayer 以存储Key和Value缓存NDArray 这些将是传递给 每次推理的状态参数 我将用Transformer 的预期形状对其进行初始化 在本例中 我转换模型时 使其期望Key和Value缓存 始终是固定大小的 以适应最大可能的上下文长度 然后当需要运行推理时 我将构建一个 MutableViews集合 包含Key和Value缓存 的两个视图 然后将它们作为 InferenceFunction.run方法的states参数提供 现在缓存将在每次推理期间 被读取并原地更新 现在使用更新后的模型 我将重新运行App 这次我可以看到 它保持了稳定的速度 不再随时间推移而减慢 在Instruments中追踪 更新后的App时 我可以确认推理延迟 的增长速度慢得多 在结束之前 我将展示一些在制作 贪吃蛇游戏时没有使用的功能 但在开发你自己的App时 可能会觉得有用 在转换贪吃蛇游戏模型时 我使用了coreai-torch包 直接转换PyTorch模块 这个流程简单 适用于许多用例 但有时你可能需要对 模型编写方式有更多控制 甚至可能控制 模型内部操作的运行方式 我们只是触及了 Core AI Python包所提供功能的表面 它还支持直接使用 Core AI API编写模型 针对Apple Silicon优化模型 以及使用Metal 4定义 自定义内核实现 要了解更多关于这些 高级模型编写流程 请参阅讲座"Dive into Core AI model authoring and optimization" 除了调试性能之外 能够调试转换后模型 的数值也至关重要 为此你可以使用 Core AI调试器 它允许你可视化 转换后的模型 轻松检查中间张量值 并追溯转换后模型中的操作 回到引入这些操作 的Python源代码 还有一个方便的 Core AI调试仪表盘 在你的App于Xcode中运行时 显示实时Core AI活动 这是发现性能问题 的好地方 在深入Instruments之前 在贪吃蛇游戏实现中 有一件事被略过了 那就是模型专化的过程
当你将AI模型随App一起发布时 那是模型的 源表示 可以在任何Apple设备上运行 然而 要在App中实际 加载和运行模型 它必须针对 App运行所在的设备进行专化 当你的模型被加载时 会检查 它是否已经被 专化并缓存 专化过程 对于非常大的模型 可能需要相当长的时间 虽然后续加载 从缓存中快速完成 但第一次可能是 你需要提前规划的 建议避免在用户 交互流程中发生模型专化 在用户交互流程中 Core AI可以帮助你解决这个问题 首先 Core AI提供 程序化访问 App的默认模型缓存 你可以请求 直接从中加载模型 如果返回nil 则说明不存在 需要进行专化 你可以用此来控制功能的访问 或告知用户 他们可能需要等待一段时间 让App准备好模型 其次 你可以在App中 明确请求模型专化 独立于加载过程 你可以在下载资源后 或用户选择启用某功能时执行此操作 这样模型就能提前准备好 还有更多可用的控制选项 SpecializationOptions有助于配置 你希望模型 如何针对推理进行优化 使用AIModelCache你还可以 删除不再需要的条目 并控制条目 持久化时长的策略 你甚至可以在同一App组中 的多个App之间共享缓存 请查阅developer.apple.com上的 "Managing model specialization and caching"文章 了解更多信息
无论专化何时发生 它都需要时间 让我们快速了解一下内部情况 在专化期间 模型会经历 两个主要转换 首先 它经历 一组核心编译步骤 对计算进行分段 规划和优化 其次 为所使用的计算单元 生成可执行工件 这些工件与生成它们的 设备和OS版本相关联 在这两个步骤中 编译 是产生大部分延迟的步骤 Core AI工具链 可以帮助你减少这段时间 通过允许一些编译在你的 开发机器上提前进行 生成模型的编译版本 虽然该编译模型仍需针对 特定用户设备进行专化 但现在需要做的工作 少得多 完成速度也快得多 要了解有关此选项的更多信息 请查阅developer.apple.com上的 "Compiling Core AI models ahead of time"文章 了解更多信息 控制专化何时 何地以及如何发生 是帮助你优化 用户体验的一种方式 你可能希望优化的 另一个领域 是消除使用模型的 紧密推理循环中的任何开销 Core AI框架有 多个API可以帮助你 你可以动态检查 NDArray参数的 最优内存布局 并使用该结构分配它们 以避免在推理时 进行布局转换
你还可以为框架预分配 要写入的输出值 以避免在推理期间 分配新的输出值
你还可以使用异步值 来高效地流水线执行 多个推理函数 对于大多数用例 高层推理API 将让你达到所需的目标 但当你在优化 紧密的推理循环 或将模型集成到 复杂的计算管道时 这些底层API 在你需要时随时可用 无论你是刚刚入门 还是深入钻研 Core AI Models代码库 都是找到所需内容的好地方 它收录了一系列热门模型 每个模型只需一条命令 即可完成转换 并针对你的App优化 AI技能专注于 Core AI模型编写 优化和转换
以及一个Swift包 包含特定模型系列的库 提供更高层的API 其中已内置了许多 底层推理优化 它还提供了一个API 用于创建Core AI语言模型 可直接插入 Foundation Models框架 让你带来自己的自定义模型 和Token采样策略 总结一下:Core AI适用于 所有Apple Silicon 帮助你在所有Apple平台上 构建前沿的AI体验 它与现有的Python工具 紧密集成 这些工具你已经 非常熟悉 以及现代Swift框架 用于在App中高效运行你的模型 以及最先进的调试工具 帮助你了解模型如何 在Apple设备上运行 我们迫不及待地想看到 你构建的各种体验
-
-
5:08 - Convert a PyTorch model to Core AI
import torch import coreai_torch # Load trained snake model and sample input for tracing pt_model = SnakeTransformer().load_checkpoint("snake.pt") example = torch.randn(1, 5, 16) # Export the torch program including dynamic shape for input sequence seq_len = torch.export.Dim("seq_len", min=1, max=256) exported = torch.export.export( pt_model, args=(example,), dynamic_shapes={"features": {1: seq_len}}, ) exported = exported.run_decompositions(coreai_torch.get_decomp_table()) # Convert torch graph → Core AI graph ai_program = coreai_torch.TorchConverter().add_exported_program( exported, input_names=["features"], output_names=["logits"], ).to_coreai() # Save as a .aimodel asset the runtime can load ai_program.save_asset("SnakeTransformer.aimodel") -
5:44 - Verify converted model numerics
import torch import numpy as np from coreai. runtime import AIModel, NDArray # Load models pt_model = SnakeTransformer().load_checkpoint("snake.pt") ai_model = await AIModel.load("SnakeTransformer.aimodel") function = ai_model.load_function("main") # Assemble input sample - 10 frames of 16-dim game features, shape (1, 10, 16) features = np.array(lextract_features(game) for - in range (10)], dtype=np.float32)[np.newaxis] # PyTorch reference with torch.no_grad(): pytorch_logits = pt_model(torch.from_numpy(features)) . numpy )[0, -1] # Core AI inference result = await function({ "features": NDArray(data=features)} ) coreai_logits = result["logits"]. numpy()[0, -1] # Validate max_diff = np.max(np.abs(pytorch_logits - coreai_logits)) assert max_diff < 0.01 -
7:41 - Core AI framework core types
// Core types within Core AI import CoreAI // Load the '.aimodel' file let model = try await AIModel(contentsOf: modelURL) // Load the main inference function let mainFunction: InferenceFunction = try model.loadFunction(named: "main")! // Construct the n-dimensional input data let inputNDArray: NDArray = nextInput() // Run inference var outputs = try await mainFunction.run(inputs: ["input": inputNDArray]) guard let outputNDArray = outputs.remove("output")?.ndArray else { // Handle unexpected missing output } -
8:33 - Initialize ModelPlayer with AIModel
// Initialize the player by loading the AIModel and InferenceFunction struct ModelPlayer { let nextActionFunction: InferenceFunction init(modelURL: URL) async throws { let model = try await AIModel(contentsOf: modelURL) self.nextActionFunction = try model.loadFunction(named: "main")! } } -
8:49 - Run inference with NDArray inputs
extension ModelPlayer: SnakePlayer { mutating func chooseAction(game: SnakeGame) async throws -> Direction { // Create an NDArray for the next input and write board features into it var inputFeatures = NDArray(shape: [game.stepCount, hiddenDim], scalarType: .float32) writeFeatures(of: game, into: inputFeatures.mutableView()) // Run inference and extract the expected logits output NDArray var outputs = try await nextActionFunction.run(inputs: ["features": inputFeatures]) guard let logits = outputs.remove("logits")?.ndArray else { throw ModelError.missingOutput } return predictedDirection(from: logits.view()) } func writeFeatures(of game: SnakeGame, into view: consuming NDArray.MutableView<Float>) { … } func predictedDirection(from logits: NDArray.View<Float>) -> Direction { … } } -
10:10 - Input features for the snake model
// Features at each time step var features = [Float]() // Distance to wall in all directions, normalized between [0, 1] features += [dWallUp, dWallDown, dWallLeft, dWallRight] // Distance to nearest food, normalized between [-1, 1] features += [dFoodX, dFoodY] // Direction encoded as one-hot: [1,0,0,0]=up, [0,1,0,0]=down, etc. features += dir.oneHotEncoding // Distance to the other snake, normalized to [-1, 1] features += [dUserX, dUserY] // Direction of the opponent snake features += dirU.oneHotEncoding -
12:18 - Add KV cache buffers to PyTorch module
# Update torch module to include key and value caches # Use register_buffer to later make the exported torch program treat them as mutable class SnakeTransformerStateful(nn.Module): def __init__(self, ...): super().__init__() self.register_buffer( "k_cache", torch.zeros(N_LAYERS, 1, MAX_SEQ_LEN, D_MODEL)) self.register_buffer( "v_cache", torch.zeros(N_LAYERS, 1, MAX_SEQ_LEN, D_MODEL)) # … -
12:50 - Update forward pass to read/write KV caches
# During forward pass, read/write KV caches class SnakeTransformerStateful(nn.Module): def forward(self, features, position_ids): new_k, new_v = [], [] for i, block in enumerate(self.blocks): # read previous keys/values from caches k_prev = self.k_cache[i] v_prev = self.v_cache[i] # ... compute q/k/v for the new token, attend over valid prefix ... new_k.append(k_updated) new_v.append(v_updated) # Update key/value caches self.k_cache.copy_(torch.stack(new_k)) self.v_cache.copy_(torch.stack(new_v)) return self.action_head(self.ln_final(x)) -
12:59 - Re-convert model with state names
# Updated coreai-torch conversion code using key/value cache states import torch import coreai_torch exported = torch.export.export( stateful_model, args=(example_features, example_position_ids), dynamic_shapes={"position_ids": {1: seq_len}}, ) exported = exported.run_decompositions(coreai_torch.get_decomp_table()) ai_program = coreai_torch.TorchConverter().add_exported_program( exported, input_names=["features", "position_ids"], state_names=["keyCache", "valueCache"], output_names=["logits"], ).to_coreai() ai_program.save_asset("SnakeTransformer.aimodel") -
13:17 - Store KV cache NDArrays in ModelPlayer
// Add stored properties for the key and value caches struct ModelPlayer { let nextActionFunction: InferenceFunction var keyCache: NDArray var valueCache: NDArray init(modelURL: URL) async throws { let model = try await AIModel(contentsOf: modelURL) self.nextActionFunction = try model.loadFunction(named: "main")! self.keyCache = NDArray(shape: [layers, maxContext, hiddenDim], scalarType: .float32) self.valueCache = NDArray(shape: [layers, maxContext, hiddenDim], scalarType: .float32) } } -
13:45 - Pass state views to inference function
extension ModelPlayer: SnakePlayer { mutating func chooseAction(game: SnakeGame, snakeID: Int) async throws -> Direction { // … var stateViews = InferenceFunction.MutableViews() stateViews.insert(&keyCache, for: "keyCache") stateViews.insert(&valueCache, for: "valueCache") // Run inference and extract the expected logits output NDArray var outputs = try await nextActionFunction.run( inputs: ["features": inputFeatures], states: stateViews) // … } } -
16:22 - Check model cache before loading
// Check if your model can be loaded from the cache let cache = AIModelCache.default guard let model = try cache.model(for: modelURL, options: .default) else { Task { @MainActor in informUser("Preparing AI features. This may take a while…") } } -
16:42 - Request model specialization
// Explicitly request specialization try await AIModel.specialize(contentsOf: modelURL)
-
-
- 0:00 - Introduction
Introduction to Core AI and an overview of what the session covers: model conversion, app integration, performance optimization, and additional features.
- 0:33 - What is Core AI
Core AI is the inference framework powering on-device Apple Intelligence, now available to developers. It covers the full model deployment lifecycle, leverages all of Apple Silicon (CPU, GPU, ANE), and comes with a modern Swift API, Python tooling, and a dedicated developer toolchain.
- 4:57 - Model conversion
How to convert a PyTorch model to the Core AI format using the coreai-torch Python package — including exporting with torch.export, specifying dynamic shapes, running the converter, and verifying numerical correctness of the converted model.
- 6:16 - App integration
How to load and run a Core AI model in your app using the CoreAI Swift framework — inspecting the model in Xcode's model viewer, initializing an AIModel, preparing inputs as NDArrays, running inference, and extracting outputs.
- 10:48 - Profiling with Instruments
How to use the new Core AI instrument in Xcode to profile model latency and identify performance bottlenecks, such as growing inference times caused by quadratic complexity in transformer models.
- 11:15 - Optimizing performance
How to eliminate inference slowdowns by adding a key-value cache as a stateful input to your model — authoring the cache in PyTorch, re-converting with state_names, and updating your app to pass MutableViews of the cache buffers at inference time.
- 14:13 - Additional features
A tour of Core AI tools not used in the demo: the rich Python authoring experience, the Core AI Debugger for numeric debugging of converted models, and the Core AI debug gauge in Xcode for streaming activity monitoring.
- 15:34 - Specialization
How Core AI specializes models for the target device — what happens during specialization, how to manage it with programmatic cache access and SpecializationOptions, and how ahead-of-time (AOT) compilation can shift work off the user's device.
- 20:07 - Next steps
Summary of Core AI's capabilities: on-device inference across all Apple Silicon, Python tooling integration, and debugging tools — with an invitation to explore the Core AI Models repository.