news 2026/9/24 5:14:59

SemIf 的 Apple Silicon 原生 MLX 后端:安装、量化、缓存复用与证据复现实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SemIf 的 Apple Silicon 原生 MLX 后端:安装、量化、缓存复用与证据复现实战指南

【免费下载链接】SemIf-OpenJev

Semantic ifs from open models, on a 3090 at home. Independent; not affiliated with Jev or TypeSafe.

项目地址:https://gitcode.com/gh_mirrors/op/SemIf-OpenJev
点击查看免费下载

SemIf(语义决策开源基线)在 macOS arm64 上通过原生 MLX 后端运行 direct(直接读分)、serial(串行前缀复用)与 shared(并行共享状态)三种决策模式,使用 MLX-LM 的 Qwen3.5 实现与 Torch 后端完全相同的提示词和答案 token 检查,且不生成任何答案 token。本文以 docs/MLX.md 为主线,结合 MLX 后端源码、CLI 入口、MLX 基准脚本 与 MLX 回归测试 等仓库证据,完整覆盖 MLX 环境的安装、semif-score评分命令的三种模式与全部 MLX 专属参数、内存量化与分配缓存控制、混合缓存正确性保证、以及从基准复现到校验的完整证据闭环,读完即可在自己的 Apple Silicon 机器上跑通并验证整条 MLX 决策流水线。

MLX 后端在 SemIf 中的定位

SemIf 的默认后端仍然是 Torch/CUDA(cli.py 中--backend默认值为torch)。MLX 后端是加性的、专门面向 Apple Silicon 的补充路径,其边界非常明确:

  • 支持:direct、serial-prefix、parallel-shared 三种决策模式,由 MLX-LM 的 Qwen3.5 原生实现驱动,与 Torch 后端共用同一套提示词构造(encode_prompt)与答案 token 槽位检查,不生成任何答案 token。
  • 不支持:MLX 的 reranker(重排器)模式被显式拒绝。在 cli.py 中有对应校验:--backend mlx --mode reranker会直接报错,reranker 必须走 Torch 后端。
  • 独立实现:浏览器 WebGPU demo 是另一套独立实现,与 MLX 后端无关。
  • 隔离性:其他平台安装时不会导入 MLX。mlx这个 extra 在 pyproject.toml 中被限定为sys_platform == 'darwin' and platform_machine == 'arm64',不会替代仓库现有的 Torch 依赖。

从源码结构看,mlx_backend.py 暴露三个与 Torch 后端同形的入口:score(direct)、SerialPrefixScorer(serial)、score_shared(shared),cli.py 在--backend mlx时用这三个函数整体替换默认实现,因此 CLI 层的命令形态对用户是一致的。

环境要求与安装

MLX 后端要求一台带有 Metal 的 Apple Silicon Mac,并使用隔离的 Python 环境:

python3 -m venv .venv source .venv/bin/activate pip install -e '.[test,mlx]'

安装有两个值得注意的硬性前提:

  1. 需要 Gitmlxextra 中的mlx-lm是以 git 依赖形式锁定的:mlx-lm @ git+https://github.com/ml-explore/mlx-lm.git@a63e24c389382619eb6d9af656e3b46024be217a(见 pyproject.toml),pip 必须借助 Git 才能解析并安装该固定提交。
  2. 平台与运行时被双重重锁mlx==0.32.2与 MLX-LM 的源码提交a63e24c389382619eb6d9af656e3b46024be217a(对应包版本 0.32.0)是生产运行时固定点,同时 manifests/mlx-validation.json 中的production_runtime也记录了这一组合,保证基准与日常评分使用的运行时完全一致。

在 mlx_backend.py 中,加载器首先校验platform.system() == "Darwin" and platform.machine() == "arm64",否则直接抛出RuntimeError;随后校验mx.metal.is_available()并把默认设备设为 GPU。MLX 的 Metal 不可用或缺少 extra 依赖都会在加载阶段被明确拒绝,而不是在推理时才神秘失败。

安装后首次评分:direct 模式

安装完成后的最小验证命令(与原文档保持一致,可直接复制运行):

semif-score --backend mlx --mode direct \ --model Qwen/Qwen3.5-4B \ --revision 851bf6e806efd8d0a36b00ddf55e13ccb7b8cd0a \ --input examples/decisions.jsonl \ --output results-mlx-direct.jsonl

几个关键的运行事实:

  • 首次运行会下载权重。固定的 checkpoint 会进入 Hugging Face 缓存,磁盘上权重约9 GB,GPU 执行还需要额外的显存(统一内存)。
  • 视觉权重被排除。源 checkpoint 含视觉权重,MLX-LM 的原生 sanitizer 会将其从文本模型中剔除,因此实际加载的是纯文本模型。
  • 精度基线是源精度:BF16,含少量 FP32 参数,不做任何额外变换。
  • 输出是新的(create-only)。CLI 强制要求输出文件不存在(cli.py 中args.output.exists()直接报错),并用open("x")独占创建,杜绝覆盖旧证据。
  • 每条预测都记录运行时来源提交mlx_lm_source),这是可追溯性的一部分。

如果你习惯用 Python 直接调用,等价路径是:

from semif_phase1 import mlx_backend model, tokenizer, metadata = mlx_backend.load_model( "Qwen/Qwen3.5-4B", "851bf6e806efd8d0a36b00ddf55e13ccb7b8cd0a") row = {"id": "route-1", "state": "Customer cannot access an account after a password reset.", "question": "Which queue should handle this request?", "options": [{"id": "access", "description": "Account access support."}, {"id": "billing", "description": "Billing support."}]} result = mlx_backend.score(model, tokenizer, row, metadata)

返回值包含probabilities(softmax 归一化的条件选项分数)、option_logitsanswer_token_idsinput_ids_sha256prompt_sha256与完整运行时元数据(model字段内含mlx_versionmlx_lm_versionmlx_lm_sourceallocator_cache_limit_bytesquantizationsource_artifact_sha256等),每条结果都会标注probability_status: conditional option score; uncalibrated as decision confidence——这是全项目一致的边界声明:输出形状有保证,语义正确性与置信度校准不保证

三种决策模式:direct / serial / shared

原文档强调,决策模式的选择取决于输入数据的形状:

# 逐行独立评分(fresh) semif-score --backend mlx --mode direct ... # 相邻行状态完全相同,串行复用前缀缓存 semif-score --backend mlx --mode serial ... # 所有行共享同一个精确状态,且决策 ID 唯一,并行共享 semif-score --backend mlx --mode shared ...

三种模式在 mlx_backend.py 中的底层实现差异如下:

模式核心入口执行策略适用场景
directscore()(L109-L122)每行独立前向,取最后一个位置的 logits 并仅抽出声明槽位状态各不相同、行间无复用价值
serialSerialPrefixScorer.score()(L150-L182)仅缓存"当前精确状态"的前缀缓存,命中时对缓存做copy.deepcopy后再续后缀,绝不原地修改保留的前缀连续行的 state 相同、后缀(问题/选项)不同
sharedscore_shared()(L185-L237)前缀只 prefill 一次,用entry.merge([entry] * len(rows))复制出独立分支,右侧补齐后一次性批量前向所有行 state 完全一致、ID 唯一、希望一次批处理全部问题

shared 模式的实现细节尤其值得展开(对应原文档的 "Cache correctness" 一节):

  • 右填充由原生循环缓存掩蔽entry.prepare(lengths=lengths, right_padding=[width - size for size in lengths])把每个后缀的真实长度交给循环(recurrent)缓存,因果注意力保证后面的 padding token 不会影响真实位置。
  • 读取每个后缀的最后一个真实 token,而不是 padding 位置:selected = [logits[i, lengths[i] - 1, mx.array(slots)] ...]
  • 分支状态绝不串味:一个问题的分支状态永远不会喂给另一个问题(deepcopy/merge 出的分支彼此独立)。
  • 共享模式的内存随批大小与后缀长度增长,因此原文档特别提醒:内存占用与 batch size、后缀长度成正比。
  • 输入长度限制不截断max_tokens超限直接抛ValueError(测试 test_token_limit_prevents_inference 验证了 "no truncation" 行为),而不是悄悄截断提示词。

缓存键是"精确 token ID"而非 Python 相等

这是一个易踩坑但被刻意设计的行为:SerialPrefixScorer的缓存命中判定基于prefix == self.prefix,而前缀由_state_prefix原始状态文本/结构化 JSON重新编码而来。这意味着:

  • 调用方在提交后修改了之前提供的 JSON 对象,不可能意外命中旧缓存——因为重新编码出的 token ID 序列已经不同;
  • 即使 Python 层面True == 1,JSON 序列化后的提示词不同,缓存也会正确失效(test_serial_keys_by_exact_tokens_not_python_value_equality 专门验证了这一场景);
  • 结构化状态变更(如把"evidence": "A deployment succeeded."改成 failed)同样会强制重建前缀缓存(test_serial_invalidates_cache_for_mutated_structured_state)。

测试如何验证混合缓存

MLX 的缓存正确性并非空口承诺。tests/test_mlx.py 使用一个真实的小型 Qwen3.5 混合模型ModelArgs(model_type="qwen3_5", ...),hidden 64、4 层、线性键值头宽 128 以满足 Metal delta 内核要求)做回归,无需下载任何权重即可验证:

  • 变长后缀、问题重排序(rows[::-1])、重复调用、状态变更、非法输入;
  • test_hybrid_cache_branches_padding_and_order断言 fresh / serial / shared 三者概率在abs=1e-4内一致,同时承认"完整 prefill 与拆分 prefill 使用不同的 Metal reduction 形状,即使 FP32 也不逐位相同";
  • test_shared_rejects_mixed_states_and_duplicate_ids验证 shared 模式对混合状态与重复 ID 的硬性拒绝。

这正是原文档"GPU 算术在不同内核、不同 prompt/batch 形状下存在差异"这一论断的测试级证据。

内存治理:MLX 分配缓存上限

这是原文档中容易被忽略但实操价值极高的一节。MLX 默认会缓存几乎全部系统内存(inactive allocation cache),在变长提示词的场景下,这会让统一内存(unified memory)被其他本地模型挤占。SemIf 的默认策略是:

  • 加载器把 MLX 的 inactive allocation cache 上限设为256 MiB(源码常量DEFAULT_CACHE_LIMIT_MIB = 256,见 mlx_backend.py);
  • 命令行可用--mlx-cache-limit-mib 512调整,或用--mlx-cache-limit-mib 0完全禁用inactive allocation caching;
  • 基准与精度探针脚本接受同一参数(mlx_benchmark.py 与 mlx_precision_probe.py 都带--mlx-cache-limit-mib);
  • Python 调用方可以在mlx_backend.load_model(..., cache_limit_mib=512)中传入;
  • 预测元数据中的allocator_cache_limit_bytes记录生效上限(字节数)。

实现上,mlx_backend.py 在加载模型前执行mx.set_cache_limit(cache_limit)cache_limit = cache_limit_mib * 1024 * 1024),这是一个进程级(process-wide)的 MLX 分配器设置,不是 prefix cache。需要澄清的边界:

  • 它约束的是分配缓存(inactive allocations),不是活跃模型或批处理的内存需求;
  • 它不限制前缀缓存(prefix cache)本身;
  • test_loader_applies_cache_limit_and_records_bytes 验证了默认 256、自定义值与字节记录逻辑;
  • test_loader_rejects_invalid_cache_limit_before_loading 验证-11.5True这类非法输入在加载前就被拒绝(必须是非负整数 MiB)。

从 results/mlx/README.md 的历史记录看,这一设置是有真实事故背景的:早期一次运行在另一个模型占用大量内存时退出(exit 137),最初 inactive-cache 上限约为 122 GiB;改为 256 MiB 上限后完整工作负载得以跑完。该文档也谨慎说明这是"可能的内存压力解释,而非独立确认的 OS 诊断"。

运行时锁定与上游归一化修复

MLX 后端的可复现性依赖严格的运行时锁定:

  • MLX 0.32.2mlx==0.32.2,且仅 macOS arm64 安装);
  • MLX-LM锁定在源码提交a63e24c389382619eb6d9af656e3b46024be217a(包版本 0.32.0),pyproject.toml 中直接以 git URL 固定,无本地 fork、无 monkey patch。

锁这个提交有实质技术原因:该提交包含上游 Qwen 循环 q/k 归一化修复。MLX-LM 0.31.3 在应用 L2 epsilon 时按 mean 计算而非 sum,导致缩放错误;修复后 epsilon 作用于 sum(参考 L2 归一化)。仓库用一个小型测试直接暴露这一点(test_recurrent_qk_normalization_matches_reference_l2_epsilon):构造数值 1e-4 的小 q/k,断言normalize_qk的结果与x * rsqrt(sum(x², axis=-1, keepdims=True) + eps)参考实现一致,且 q 额外乘width**-0.5的缩放。

该修复对概率输出的影响在 results/mlx/README.md 的 "Precision investigation" 中有量化记录:在 12 个超过 0.05 审查阈值的质量决策上,原生 MLX 权重转 FP32 对比 CPU FP32 的最大概率差从0.109083 降到 0.009402;源码 RMSNorm 权重在 FP32 下折叠后对比也从0.101279 降到 0.007598

另外,每条预测都会记录运行时的源码提交mlx_lm_source,来自distribution("mlx-lm").read_text("direct_url.json")),因此即使未来有人换了运行时,历史证据也能被追溯。

内存量化:--mlx-bits 4 / 8

MLX 后端支持不落盘的内存量化:

semif-score --backend mlx --mode direct --mlx-bits 8 ... semif-score --backend mlx --mode direct --mlx-bits 4 ...

关键事实(原文档 + 源码双重确认):

  • --mlx-bits 8--mlx-bits 4应用确定性仿射量化(deterministic affine quantization),group size64
  • 它从同一个锁定 checkpoint出发,在内存中调用nn.quantize(model, group_size=64, bits=bits, mode="affine")(mlx_backend.py),不写出另一个模型文件
  • 结果记录该变换与源文件哈希(source_artifact_sha256);
  • 量化会改变模型概率,必须与未量化 MLX 运行分开独立评估,绝不允许靠改容差来保留 CUDA 的精度声明(manifests/mlx-validation.json 的quantization_policy明文规定);
  • 量化要求未量化源 checkpoint:若源 config 已含quantizationquantization_config,加载器直接拒绝(mlx_backend.py);
  • bits只接受None/4/8,其他值报错(mlx_backend.py);--mlx-bits只有在--backend mlx时才合法(cli.py)。

量化带来的实测内存与行为权衡(来自 results/mlx/README.md 的量化结果表,BF16 为默认基线):

精度Authored144 平衡准确率Perturbations108 平衡准确率变更选项(authored / perturbations)质量评分峰值 MLX 内存
BF1681.32%77.99%0 / 08.12 GiB
Q881.85%76.58%2 / 44.66 GiB
Q478.92%79.89%14 / 62.82 GiB

同一文档明确警告:8-bit 变更了 6/252 个决策(最大概率移动 0.1761),4-bit 变更了 20/252 个(最大移动 0.6776),"这些小型 authored fixture 不足以支撑通用质量排名,量化是可测量的内存/行为权衡;4-bit 适合内存实验,但会显著改变部分分布"。此外,量化模型在压缩生成对比中三次都只生成 20/21 个答案(作为失败完成证据保留,而非等价性能),direct 路径则每次都返回全部决策。

本地模型目录与修订标签

原文档强调的加载约束在源码中是这样落地的(mlx_backend.py):

  • 远程模型revision必须是不可变的 40 字符 commit ID(正则[0-9a-f]{40}),mainlatest这类可变标签会被拒绝——test_remote_model_requires_immutable_revision验证了这一行为;
  • 本地模型目录:要求提供修订标签,且本地目录同样会被逐文件 SHA-256 哈希(source_artifact_sha256),"用户自报的本地修订号不是唯一的溯源依据"。

模型加载的硬性约束:原生 Qwen3.5 文本评分

与 Torch 后端一致,MLX 加载器禁止自定义模型代码(mlx_backend.py):

  • config.json中出现model_file(自定义代码路径)或model_type不是qwen3_5时直接抛ValueError
  • 加载使用tokenizer_config={"trust_remote_code": False},杜绝远程代码执行面;
  • test_loader_rejects_custom_or_unsupported_models 覆盖了model_file: "custom.py"与未知model_type两种拒绝场景。

Apple Silicon 演示与可回放录制

上图为完整录制的一次真实本地 CLI 运行(历史项目名 OpenJev 时期)。录制细节见 docs/media/README.md:终端录制文件为 docs/media/openjev-mlx.cast(asciicast v2 格式),可用asciinema play openjev-mlx.cast回放真实进程输出与时间轴,并非模拟动画。录制环境为 Apple M5 Max、128 GiB 统一内存、MLX 0.32.2、锁定 MLX-LM 0.32.0;当前命令统一使用semif-score。录制中的 6.36 秒墙钟时间包含加载与哈希,属于 CLI 演示而非吞吐基准;条件选项分数未校准。

当前版本在该环境下的复现命令:

semif-score --backend mlx --mode direct \ --model Qwen/Qwen3.5-4B \ --revision 851bf6e806efd8d0a36b00ddf55e13ccb7b8cd0a \ --input examples/decisions.jsonl \ --output results-mlx-demo.jsonl

examples/decisions.jsonl即演示所用的固定样例,演示用的真实输出保留在 docs/media/openjev-mlx-results.jsonl(含模型修订、源哈希、概率与时间元数据),可用gzip/shasum思路自行核对。

压缩保留的证据与透明读取

为了保持仓库体积,大量历史行级报告被无损 gzip 压缩.json.gz/.jsonl.gz),而摘要、manifest、CLI 证据与本文档保持明文。关键设计(源码见 benchmarks/mlx_evidence.py):

  • 验证与参考运行读取器透明接受.gz文件read_bytes()优先解析原始文件名,若仅有.gz版本则透明解压;若同目录同时存在明文与压缩版本会拒绝歧义并抛错;
  • 新的基准运行保持原始明文 JSON/JSONL 格式,不新写压缩文件;
  • 原始载荷校验和与复现细节在 证据索引(该 README 同时是原文档../results/mlx/README.md的仓库根路径形式)。

查看一个保留的压缩报告:

gzip -dc results/mlx/2026-09-17-bf16-fixed/quality.json.gz

校验解压后载荷的原始哈希(不解压落盘):

python benchmarks/mlx_evidence.py results/mlx/UNCOMPRESSED_SHA256SUMS

该脚本输出{"verified_original_payloads": N, "status": "ok"},逐个核对 30 个保留运行文件的原始载荷 SHA-256。

复现基准证据:mlx_benchmark.py

从仓库根目录、环境激活后运行。每个输出目录必须是全新的args.output.mkdir(parents=True, exist_ok=False),存在即失败);中断的运行保留为部分证据而不会被覆盖;GPU 基准一次只跑一个进程(统一内存环境避免相互干扰)。

python benchmarks/mlx_benchmark.py --suite diagnostic --output results/mlx/my-pilot python benchmarks/mlx_benchmark.py --suite all --output results/mlx/my-bf16 python benchmarks/mlx_benchmark.py --suite quantization --bits 8 --reference-run results/mlx/my-bf16 --output results/mlx/my-q8 python benchmarks/mlx_benchmark.py --suite quantization --bits 4 --reference-run results/mlx/my-bf16 --output results/mlx/my-q4

各 suite 的记录内容(与 mlx_benchmark.py 的diagnostic/quality/shape/generation函数一一对应):

  • Diagnostic(诊断):精确 tokenizer 等价性(MLX tokenizer 与原始 transformers tokenizer 逐字节对比,不一致即AssertionError);小规模 pilot 上的 fresh、serial、parallel、重排序评分对比。
  • Quality(质量):144 条 authored 与 108 条 perturbation 用例,复用现有评估器指标、missing-evidence 行为、与已发布 Torch 预测的行级差异(compare()输出 argmax 翻转、提示词不匹配、最大概率差与均值)。注意:已发布 Torch 预测使用了串行前缀复用,因此该对比包含后端差异与执行形状差异,不是单一变量对照。
  • Systems(系统,777 决策):37 个 state × 21 个决策,在 fresh / serial / parallel 三种模式下全覆盖,记录每 state 延迟、决策/秒、峰值 MLX 分配,以及与 fresh 的每个选项变更。shape()内先硬校验len(rows) == 777len(groups) == 37、每组 21 条,不符合即报错。
  • Generation(生成对比):3 次重复,比较 21 条 direct 分布与同一模型写出紧凑 yes/no 数组(compact_messages,仅有序"yes"/"no"值,无键、无置信度对象、无解释)。记录原始输出、有效性(必须是完整有序数组,缺失即失败)、首 token 时间、完成时间与一致性。无效生成答案是失败,不是"等价更快/更慢"的答案——这个判定哲学与全项目一致。

量化套件(quantization)运行 diagnostic、quality、generation 对比;--suite all --bits 8(或 4)则额外把全部 777 决策以三种模式跑完该精度。量化套件要求--bits且必须指定--reference-run(未量化 MLX 证据目录),并且 reference 的 model/revision 必须与当前运行完全一致(mlx_benchmark.py 校验 manifest 后还会记录 reference 文件哈希)。

计时边界:哪些时间被算进去了?

原文档对计时范围有严格声明,源码中 manifest 的timing_scope字段也原样记录:

  • 不计时:模型加载、artifact 哈希、初始 warmup、结果写入;
  • 计时:prompt 渲染、tokenization、evaluation、同步、CPU 读出(即从预热后的 GPU 执行开始,含 CPU 读出);
  • MLX 是惰性的mx.eval(...)求值数组、mx.synchronize()等待 GPU 完成是计时的必要组成部分(score_prefillscore_shared中都成对出现);
  • 峰值 MLX 分配包含模型权重与临时数组,不是macOS 进程总内存,也不直接等同于CUDA 的分配器指标;CUDA 与 Mac 的计时描述的是不同硬件。

实测参考(来自 results/mlx/README.md,Apple M5 Max、128 GiB、BF16、修复后运行时):direct 475.59 s(1.63 decisions/s)、serial 66.84 s(11.62/s,相对 fresh 提速 7.12×)、shared 45.06 s(17.24/s,提速 10.55×);serial 与 shared 各变更 3/777 个决策,且每个变更决策在至少一种执行形状下是 0.5/0.5 平局,最大概率移动分别为 0.06713 / 0.06028。这是"测得的提速伴随微小数值差异",不是位级一致的复用。质量套件中 252 个决策全部与已发布 Torch 预测选择相同胜出选项(authored144 81.32%、perturbations108 77.99%)。

验证命令与证据完整性

原文档给出的验证命令在仓库中均有对应物:

pytest -q (cd results/raw && shasum -a 256 -c SHA256SUMS) (cd results/mlx && shasum -a 256 -c SHA256SUMS) python benchmarks/mlx_evidence.py results/mlx/UNCOMPRESSED_SHA256SUMS python benchmarks/verify_published.py python benchmarks/verify_mlx.py results/mlx/my-bf16
  • pytest -q覆盖 tests/test_mlx.py 的全部混合缓存回归(非 Apple Silicon 机器上会自动跳过,见模块级pytest.skipimportorskip);
  • 两份SHA256SUMS分别校验已发布 CUDA 原始证据与 MLX 保留压缩字节;
  • verify_mlx.py对每个最终基准目录做结构化校验,保留的 bf16-fixed、q8-fixed、q4-fixed 三个目录全部通过;
  • 原始 CUDA 摘要与证据保持原样未被修改;Mac 测量数据放在results/mlx/下各自带日期的目录中。

精度探针:深入调查概率差异

如果在基准中发现超过阈值的概率差异,可以在 GPU 计时基准结束后运行 FP32 诊断(CPU 参考工作会共享 Mac 内存,务必在计时基准之后运行):

python benchmarks/mlx_precision_probe.py \ --run results/mlx/my-bf16 --output results/mlx/my-bf16/precision-probe.json

mlx_precision_probe.py 的工作方式(对应原文档描述):

  1. 读取 manifests/mlx-validation.json 中冻结的审查阈值bf16_probability_difference_review_threshold(0.05),选择超过阈值的质量行以及任何变更选项的行
  2. 用相同提示词对比原生 BF16 与 FP32 的 MLX 执行,并加载 PyTorch CPU FP32 参考(Qwen3_5ForCausalLMdtype=torch.float32device_map={"": "cpu"},加载不完整会直接RuntimeError);
  3. 额外做一个单独的权重折叠诊断:MLX-LM 会把 Qwen 的(1 + RMSNorm weight)折叠成常规 RMSNorm 权重,BF16 下折叠先舍入加法、后续 FP32 转型会保留该舍入;探针用 FP32 源数组走同一 sanitizer 隔离这一"权重转换舍入"与"执行舍入";
  4. 输出比较报告(bf16_mlx_vs_publishedfp32_mlx_vs_cpubf16_mlx_vs_fp32_mlx等),并明确声明限制:"从观测差异中选择的诊断,不是留出法质量基准;CPU 计时不与 GPU 计时比较"。

该探针不改变生产模型加载方式,也不把诊断结果冒充基准。对 results/mlx/README.md 中记录的 12 个超阈值决策,修复后运行时把与 CPU FP32 的最大概率差从 0.109 级降到 0.009 级。

验证契约与政策

manifests/mlx-validation.json 冻结了 MLX 验证的全部契约,值得逐条解读:

  • exact_contracts:prompt 文本、输入 token ID、答案 token ID、决策 ID、选项 ID、有限归一化概率、完整 fixture 覆盖——这些必须逐位一致;
  • tiny_fp32_cache_probability_atol: 0.0001:小型 FP32 缓存回归的容差(测试用pytest.approx(..., abs=1e-4));
  • bf16_policy:"报告每个变更选项;超过审查阈值要调查,它是诊断触发条件而非准确率/校准保证;不要求 CUDA 与 Metal 逐位一致";
  • runtime_note:原始 pilot 用 0.31.3,生产固定上游 L2 epsilon 修复,容差不变——修复不通过放宽容差来"洗白"差异;
  • bf16_policyquantization_policy共同划定了全项目的证据边界:typed output 不保证语义正确性,softmax 分数不是校准置信度

边界与限制速查

综合原文档与仓库证据,使用 MLX 后端前应记住以下边界:

  • reranker 模式在 MLX 下不可用(CUDA-only);
  • PyTorch MPS 与 MLX 是两条独立 Apple 路径,docs/APPLE_SILICON.md 记录了 MPS 在混合注意力上会回退参考内核(causal_conv1dflash-linear-attention仅 CUDA),MPS 不应直接对标已提交的 RTX 3090 数字;
  • MLX 输出是条件选项分布:保证输出形状,不保证正确决策或校准置信度;精度、内核形状、前缀拆分、批大小的变化都可能移动分数并改变接近的决策,所有观测到的胜出选项变更都保留在比较报告中;
  • 已发布的 CUDA 数字不受影响,两个 Apple 后端都是加性的。

结语:一条可追溯的 Apple Silicon 决策基线

SemIf 的 MLX 后端不是简单地把模型搬到 Metal,而是一整套"固定运行时 + 原生混合缓存 + 确定性量化 + 冻结阈值 + 可复现证据"的工程闭环:加载器在源头拒绝自定义模型与可变修订,score_shared用原生循环缓存的分支合并与右填充掩蔽保证并行共享正确性,分配缓存上限防止统一内存被 MLX 默认策略吃满,manifest 记录每次运行的运行时源码提交与源文件哈希,基准与探针只写新目录,校验命令逐字节核对压缩证据。按照本文的安装、评分、基准与验证步骤,你可以在自己的 Apple Silicon 机器上完整复现这份证据链,并用 results/mlx/README.md 中的实测数据(M5 Max、BF16、777 决策全模式对比、量化权衡)作为自己环境下的参照基准。

【免费下载链接】SemIf-OpenJev

Semantic ifs from open models, on a 3090 at home. Independent; not affiliated with Jev or TypeSafe.

项目地址:https://gitcode.com/gh_mirrors/op/SemIf-OpenJev
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 5:08:11

Nginx UI Auth 配置指南:IP 白名单、可信代理与登录安全策略

后端前端运维MCP 服务 【免费下载链接】nginx-ui Yet another WebUI for Nginx 项目地址: https://gitcode.com/gh_mirrors/ngi/nginx-ui 点击查看 免费下载 本指南系统讲解 Nginx UI 从 v2.0.0-beta.26 起引入的 auth 配置段(见 docs/guide/config-aut…

作者头像 李华
网站建设 2026/9/24 5:05:14

【SSM课程设计/毕业设计】基于 SSM+Vue 的电脑配件商品检索系统的设计与实现 基于 SSM+Vue 的电脑配件购物车管理系统【附源码、数据库、万字文档】

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

作者头像 李华
网站建设 2026/9/24 5:02:15

2026年做个人服务选哪个厂家更专业靠谱?

2026年个人服务市场竞争加剧,不管是选品质管控体系搭建、全链路品质优化还是客诉风险预防的服务,找对专业靠谱的厂家是核心。结合新能源、制造业头部企业的实战经验,推荐以周瑜为核心的专业质量管理服务团队,判断标准和实操建议如…

作者头像 李华
网站建设 2026/9/24 4:50:10

Flutter鸿蒙化适配:screen_protector防截屏插件ArkTS实现指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华