coremltools 实战:将 OpenELM 语言模型转换为 Core ML 并逐 token 生成文本 AI 应用机器学习模型优化【免费下载链接】coremltoolsCore ML tools contain supporting tools for Core ML model conversion, editing, and validation.项目地址https://gitcode.com/gh_mirrors/co/coremltools点击查看免费下载导读本文演示如何使用 coremltools 将苹果开源的 PyTorch 小规模语言模型 OpenELMOpen Efficient Language Model转换为 Core ML 格式并在 macOS 上验证转换前后模型的推理一致性。你将掌握一套完整的 LLM 端侧落地流程加载模型与分词器、通过torch.export导出计算图、调用ct.convert转换为 ML Program、以及编写逐 tokentoken-by-token自回归解码脚本对比 PyTorch 与 Core ML 的输出文本。背景为什么选择 OpenELMOpenELM 是一族开源的高效语言模型Open Efficient Language Models其中小尺寸变体如 270M 参数级别体积小、推理开销低非常适合移动端与嵌入式语言应用场景。本示例选用其最小的指令微调变体OpenELM-270M-Instruct该模型复用 Llama-2-7B 的分词器因此无需额外训练即可直接走「导出 → 转换 → 部署」的标准链路。示例的完整代码与输出可对照仓库文档 convert-openelm.md 阅读。环境要求与版本前提本示例的运行前提如下macOS Monterey12或更高版本PyTorchHugging Face TransformersCore ML Tools 8.0 或更高版本。安装依赖pip install torch pip install transformers pip install coremltools文档创作时的参考环境为torch 2.5.1 transformers 4.46.3 coremltools 8.1需要特别说明的是coremltools 对 torch.export 路径的支持与 PyTorch 版本强相关。从仓库依赖检测代码 coremltools/_deps/init.py 可以看到仅当torch 2.5.0时_HAS_TORCH_EXPORT_API才为Truect.convert才会走 ExportedProgram 前端TorchFrontend.TORCHEXPORT仓库同时声明了 PyTorch 最大支持版本_TORCH_MAX_VERSION 2.8.0超出会打印版本告警若环境中缺少 torch.export API转换时会提示Torch.Export API not found。因此建议将 torch 保持在 2.5.0 ~ 2.8.0 之间并搭配 coremltools 8.x 使用。导入库并初始化模型首先导入torch、numpy、coremltools并从transformers导入AutoModelForCausalLM与AutoTokenizerimport torch import numpy as np import coremltools as ct from transformers import AutoModelForCausalLM, AutoTokenizer初始化语言模型与分词器。选择最小的指令变体 OpenELM-270M-Instruct并显式指定torch_dtypetorch.float32转换阶段统一使用 fp32避免低精度引入的数值偏差由于 OpenELM 的自定义实现依赖远程代码需要设置trust_remote_codeTruetorch_model AutoModelForCausalLM.from_pretrained( apple/OpenELM-270M-Instruct, torch_dtypetorch.float32, trust_remote_codeTrue, return_dictFalse, use_cacheFalse, ) torch_model.eval() tokenizer AutoTokenizer.from_pretrained(meta-llama/Llama-2-7b-hf)几个关键参数的含义参数取值作用torch_dtypetorch.float32固定模型权重精度保证转换与比对基准一致trust_remote_codeTrue允许执行 OpenELM 在 Hugging Face 上发布的自定义模型代码return_dictFalse让forward直接返回张量元组便于后续循环中取[0]作为 logitsuse_cacheFalse关闭 KV 缓存避免导出图中出现不可控的动态分支简化图结构使用 torch.export 导出模型自 Core ML Tools 8.0 起官方推荐的 PyTorch 转换输入是torch.export产生的ExportedProgram详见 model-exporting.md。导出过程会用示例输入真正跑一遍模型捕获输入张量流经各层时实际执行的算子序列。example_input_ids torch.zeros((1, 32), dtypetorch.int32) sequence_length torch.export.Dim(namesequence_length, min1, max128) dynamic_shapes {input_ids: {1: sequence_length}} exported_program torch.export.export( torch_model, (example_input_ids,), dynamic_shapesdynamic_shapes, )要点解析示例输入(1, 32)表示 batch 为 1、序列长度 32 的 token id 张量dtype 为int32torch.export.Dim(namesequence_length, min1, max128)声明了一个取值范围在 1~128 之间的动态维度即序列长度维dynamic_shapes {input_ids: {1: sequence_length}}将其绑定到input_ids的第 1 维。这样转换出的 Core ML 模型输入就是一个带范围约束的灵活形状在 Core ML 中对应RangeDim概念可以接受任意 1~128 长度的输入序列关于动态形状torch.export路径与旧的torch.jit.trace路径有一个本质差异tracing 本身没有「动态」概念动态形状要在ct.convert阶段用TensorType/RangeDim指定并传播而torch.export在导出时就严格表达动态性转换时若目标就是RangeDim则无需任何额外配置它会自动从torch.export.Dim转换而来。转换为 Core ML 模型将导出的exported_program直接交给ct.convert即可mlmodel ct.convert(exported_program)这一步的简洁是有底层支撑的与 trace 路径不同ExportedProgram 本身携带了完整的输入名称、形状与 dtype 信息因此用户不再需要传inputs参数coremltools 会自动为每个输入创建对应的TensorType/RangeDim/StateType。从源码看ct.convert会经由 coremltools/converters/_converters_entry.py 分发到 PyTorch 前端最终由 coremltools/converters/mil/frontend/torch/load.py 的load识别ExportedProgram并构造TorchConverter。转换内部还有几个值得注意的机制方言检查TorchConverter在 converter.py 中检查loaded_model.dialect——ATEN走TorchFrontend.TORCHEXPORTEDGE走TorchFrontend.EXECUTORCH其余方言直接抛出NotImplementedError输入一致性校验若用户显式传了inputs转换器会逐一与 ExportedProgram 中的输入形状比对见_match_user_exir_inputs静态维度的尺寸不一致会报ValueError仓库测试test_invalid_inputs即验证了这一点见 test_torch_export_conversion_api.py状态state处理torch.export 中可变的 buffer 会被自动提取为 Core ML state且统一转为 fp16见 converter.py。对提示词进行分词为了验证转换后模型的性能先用分词器处理一段提示词Once upon a time there was并把 token id 列表转成 Torch 张量prompt Once upon a time there was tokenized_prompt torch.tensor(tokenizer(prompt)[input_ids]) # Since model takes input ids in batch, # create a dummy batch dimension (i.e. size 1) for tokenized prompt tokenized_prompt tokenized_prompt.unsqueeze(0)注意模型要求 batch 形式的输入因此用unsqueeze(0)在序列前补一个大小为 1 的 batch 维最终形状为(1, seq_len)。运行原始 PyTorch 模型基准以分词后的提示词为输入运行原始 PyTorch 模型作为性能与正确性的基准。这里采用最朴素的贪心解码每次只取 logits 中概率最高的 token 追加到序列末尾循环直到达到最大序列长度最后解码回文本。max_sequence_length 64 input_ids tokenized_prompt # extend sentence (sequence) word-by-word (token-by-token) # until reach max sequence length for i in range(max_sequence_length): logits torch_model(input_ids)[0] # determine the next token by greedily choosing the one with highest logit (probability) output_id torch.argmax(logits, -1)[:, -1 :] # append the next token to sequence input_ids torch.cat((input_ids, output_id), axis-1) # decode tokens back to text output_text tokenizer.decode(input_ids[0].tolist(), skip_special_tokensTrue) print(Output text from the original torch model:) print(output_text)Output text from the original torch model: Once upon a time there was a man named John Smith. John Smith was a hard-working farmer, a good husband, and a good father. He loved his family dearly, and he cherished every moment with them. But one day, Johns life changed forever. Johns wife, Mary, died suddenly of运行转换后的 Core ML 模型接下来用相同输入运行转换后的 Core ML 模型逻辑与 PyTorch 版完全对偶把张量换成 NumPy 数组把torch_model(...)换成mlmodel.predict({input_ids: input_ids})torch.argmax换成np.argmaxtorch.cat换成np.concatinput_ids np.int32(tokenized_prompt.detach().numpy()) # extend sentence (sequence) word-by-word (token-by-token) # until reach max sequence length for i in range(max_sequence_length): logits list(mlmodel.predict({input_ids: input_ids}).values())[0] # determine the next token by greedily choosing the one with highest logit (probability) output_id np.argmax(logits, -1)[:, -1 :] # append the next token to sequence input_ids np.concat((input_ids, output_id), dtypenp.int32, axis-1) # decode tokens back to text output_text tokenizer.decode(input_ids[0].tolist(), skip_special_tokensTrue) print(Output text from the converted Core ML model:) print(output_text)Output text from the converted Core ML model: Once upon a time there was a man named John Smith. John Smith was a hard-working farmer, a good husband, and a good father. He loved his family dearly, and he cherished every moment with them. But one day, Johns life changed forever. Johns wife, Mary, died suddenly of要点解析预测接口mlmodel.predict({input_ids: input_ids})的输入键名与 ExportedProgram 的输入名一致输出是一个字典示例通过list(...values())[0]取唯一输出logits作为下一 token 的分布dtype 对齐输入必须与转换时的 dtype 一致即np.int32因此用np.int32(...)显式转换自回归循环np.argmax(logits, -1)[:, -1 :]取最后一个位置当前序列末尾上得分最高的 token id再np.concat(..., axis-1)沿序列维拼接循环 64 次即生成长度为「提示词 64」的序列。从输出可以看到转换后的 Core ML 模型与原始 PyTorch 模型生成了完全相同的文本说明转换在数值与行为上是一致的。深入转换链路在仓库中的实现为了让上文所述流程更有据可查这里给出仓库中对应的实现证据方便你在实际排查问题时继续深入前端入口load.py 中定义了_torchexport_from_spec支持直接传入内存中的ExportedProgram对象也支持加载序列化到磁盘的.pt2文件torch.export.load若目标是EXECUTORCH前端且 dialect 不是EDGE还会先调用executorch.exir.to_edge转换方言核心转换器converter.py 的TorchConverter.__init__根据loaded_model.dialect选择TORCHEXPORT/EXECUTORCH前端随后通过InternalTorchIRGraph.from_exir把 ExportedProgram 的图节点翻译成 MIL 内部图算子翻译模型中的torch.argmax、torch.cat等算子都有对应翻译实现例如 ops.py 中的argmax同时注册argmin别名处理dim/keepdim参数与 ops.py 中的cat会先过滤 PyTorch 允许但 Core ML 不允许的空张量再生成mb.concat测试佐证仓库的 test_torch_export_conversion_api.py 覆盖了动态输入转换断言转换后的shapeRange上下界与torch.export.Dim一致、枚举形状ct.EnumeratedShapes、静态形状物化等多种场景可作为你转换 LLM 时处理输入形状的参考testing_utils.py 展示了官方的导出→转换测试套路torch.export.export后调用run_decompositions({})再进入转换这与本文的导出流程一致。小结与延伸阅读至此你已走通「OpenELM 加载 → torch.export 导出 → ct.convert 转换 → 逐 token 贪心解码」的完整链路并验证了 Core ML 转换版与原模型输出一致。这套流程对 OpenELM 家族的其他变体以及基于 Transformers 架构的类似因果语言模型同样适用只需替换from_pretrained的模型标识即可。关于 torch.export 转换路径还需要了解它的成熟度边界Core ML Tools 8.0 中该路径处于 beta 状态与 PyTorch export API 的状态保持一致官方说明其总算子翻译测试覆盖率约 70%并推荐先在当前可用的torch.jit.trace模型上逐步迁移验证详见 model-exporting.md。如果你的模型在转换或运行时遇到问题可以参考该文档中的限制说明以及本仓库的测试用例进行排查。赞分享AI 应用机器学习模型优化【免费下载链接】coremltoolsCore ML tools contain supporting tools for Core ML model conversion, editing, and validation.项目地址https://gitcode.com/gh_mirrors/co/coremltools点击查看免费下载相关推荐深入解析 rippled 的 SHAMapXRP Ledger 的 Merkle 基数树数据核心深入解析 rippled 的 SHAMapXRP Ledger 的 Merkle 基数树数据核心 SHAMapSHA Map / Shared MerkleAI 应用机器学习模型优化CoreMLTools终极指南如何将TensorFlow/PyTorch模型快速转换为Apple Core ML格式CoreMLTools终极指南如何将TensorFlow/PyTorch模型快速转换为Apple Core ML格式 想要在iPhone、iPad或MaAI 应用机器学习模型优化NocoBase CRM 2.0 解决方案模块化销售管理系统与 AI 员工实战指南NocoBase CRM 2.0 解决方案模块化销售管理系统与 AI 员工实战指南 导读本文基于 NocoBase 低代码平台官方 CRM 2.0 解决方案AI 应用机器学习模型优化上一篇CANN ops-math DropOutDoMaskV3D 算子深度解析位掩码 Dropout 反向梯度缩放原理、参数约束与图模式调用实战下一篇boneyard-js 骨骼屏生成框架实战指南DOM 快照转矩形 Bones 的多框架 Skeleton 方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考