<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jan</title>
    <description>The latest articles on DEV Community by Jan (@janmusic).</description>
    <link>https://dev.to/janmusic</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4205033%2Fd8156ba6-95d8-461e-b85c-0fe044a096f2.png</url>
      <title>DEV Community: Jan</title>
      <link>https://dev.to/janmusic</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/janmusic"/>
    <language>en</language>
    <item>
      <title>Running released Next-Concept Modeling checkpoints on Apple Silicon</title>
      <dc:creator>Jan</dc:creator>
      <pubDate>Fri, 30 Oct 2026 15:37:24 +0000</pubDate>
      <link>https://dev.to/janmusic/running-released-next-concept-modeling-checkpoints-on-apple-silicon-28mi</link>
      <guid>https://dev.to/janmusic/running-released-next-concept-modeling-checkpoints-on-apple-silicon-28mi</guid>
      <description>&lt;p&gt;You can run HazyResearch's released Next-Concept Modeling checkpoints on the Apple Silicon GPU without installing PyTorch. &lt;a href="https://pypi.org/project/ncm-mlx/0.2.0/" rel="noopener noreferrer"&gt;NCM-MLX 0.2.0&lt;/a&gt; supports unconditional TinyStories generation and the released easy, medium and hard Sudoku models. The project provides converted weights separately on Hugging Face, an independent MLX inference implementation and recorded comparisons with upstream PyTorch. Model weights are not included in the Python package.&lt;/p&gt;

&lt;p&gt;The research method and trained checkpoints are by &lt;a href="https://huggingface.co/papers/2610.14162" rel="noopener noreferrer"&gt;Kumbong et al.&lt;/a&gt;. NCM-MLX independently implements their inference computation; the original training results have not been reproduced here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try a released checkpoint
&lt;/h2&gt;

&lt;p&gt;In a Python 3.12 environment on Apple Silicon:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"ncm-mlx==0.2.0"&lt;/span&gt; huggingface-hub
hf download janmusic/ncm-tinystories-mlx &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--revision&lt;/span&gt; be91c549d5336dd64a476ace9c27589a6f21a7fc &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--local-dir&lt;/span&gt; ncm-tinystories-mlx
python &lt;span class="nt"&gt;-m&lt;/span&gt; ncm_mlx official generate &lt;span class="nt"&gt;--task&lt;/span&gt; tinystories &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--weights&lt;/span&gt; ncm-tinystories-mlx &lt;span class="nt"&gt;--n&lt;/span&gt; 2 &lt;span class="nt"&gt;--levels&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://huggingface.co/janmusic/ncm-tinystories-mlx" rel="noopener noreferrer"&gt;model card&lt;/a&gt; documents the downloaded files and their provenance. The model stores approximately 500 MB of float32 weights, plus tokenizer and metadata. It uses its own inference path; it is not a Transformers &lt;code&gt;AutoModelForCausalLM&lt;/code&gt; or an &lt;code&gt;mlx_lm&lt;/code&gt; checkpoint.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--levels&lt;/code&gt; decodes the accumulated latent representation after each level. TinyStories uses a 12-level hierarchy: level 0 is drawn from a stored unigram distribution, then 11 generator forwards predict the remaining levels in parallel. Output is bounded to 128 BPE token positions. This checkpoint generates unconditionally from a start prefix; it does not accept instructions or continue a user prompt.&lt;/p&gt;

&lt;p&gt;For a preview without installation, the &lt;a href="https://huggingface.co/spaces/janmusic/ncm-refinement-demo" rel="noopener noreferrer"&gt;browser demo&lt;/a&gt; replays recorded refinement traces. It is a static replay of actual outputs, not live inference. Intermediate text can be incoherent: it shows partial reconstruction, not the model's reasoning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Converting the checkpoints
&lt;/h2&gt;

&lt;p&gt;The upstream release contains PyTorch checkpoints and pickled tokenizer data. The &lt;a href="https://github.com/janmus1c/ncm-mlx/blob/v0.2.0/ncm_mlx/official/convert.py" rel="noopener noreferrer"&gt;converter&lt;/a&gt; pins checkpoint revision &lt;code&gt;d342ce8aaea58830eef3b8ad473664673e41f692&lt;/code&gt; and checks source-file SHA-256 digests before parsing. An alternative to downloading the converted weights is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; ncm_mlx official fetch &lt;span class="nt"&gt;--task&lt;/span&gt; tinystories
python &lt;span class="nt"&gt;-m&lt;/span&gt; ncm_mlx official convert &lt;span class="nt"&gt;--task&lt;/span&gt; tinystories
python &lt;span class="nt"&gt;-m&lt;/span&gt; ncm_mlx official generate &lt;span class="nt"&gt;--task&lt;/span&gt; tinystories &lt;span class="nt"&gt;--n&lt;/span&gt; 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A small &lt;a href="https://github.com/janmus1c/ncm-mlx/blob/v0.2.0/ncm_mlx/official/torch_reader.py" rel="noopener noreferrer"&gt;tensor reader&lt;/a&gt; reads the zip container, tensor-storage buffers and reconstruction metadata without importing PyTorch. Its restricted unpickler permits supported storage types, tensor reconstruction and an ordered dictionary, and rejects unrecognized pickle globals. The separate BPE reader refuses every global. This is a deliberately narrow reader for pinned files, not a general replacement for arbitrary checkpoint loading.&lt;/p&gt;

&lt;p&gt;Each source tensor must match exactly one conversion rule. The rule either maps it to an expected MLX parameter, transforms its layout, or explicitly identifies state that inference does not read. Ordinary convolution weights change from &lt;code&gt;[out, in, kernel]&lt;/code&gt; to &lt;code&gt;[out, kernel, in]&lt;/code&gt;; learned strided resampling weights use their own transpose-and-reshape rules. A shape-compatible but wrongly ordered tensor could otherwise produce plausible-looking failures.&lt;/p&gt;

&lt;p&gt;The converter checks missing and unexpected parameters and rejects shape mismatches. It also checks the stored level IDs and attention mask before dropping those derived buffers; EMA training state is omitted. The output is safetensors plus an architecture specification, tokenizer JSON and, for TinyStories, the level-0 distribution. This is a file-format and layout conversion, not quantization or additional training.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checking tokenization
&lt;/h2&gt;

&lt;p&gt;TinyStories uses the released 8,192-token byte-pair tokenizer. Replacing it with another tokenizer of similar vocabulary size would change every subsequent input. The independent &lt;a href="https://github.com/janmus1c/ncm-mlx/blob/v0.2.0/ncm_mlx/official/bpe.py" rel="noopener noreferrer"&gt;BPE implementation&lt;/a&gt; reproduces pre-tokenization, byte handling, merges and special tokens.&lt;/p&gt;

&lt;p&gt;Thirteen retained parity cases, including Unicode, have identical pre-tokenization, token IDs and round trips. The checks do not exhaust Unicode inputs; rare whitespace classes remain a documented limitation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparing outputs and logits
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/janmus1c/ncm-mlx/blob/v0.2.0/docs/research/official-checkpoint-parity.json" rel="noopener noreferrer"&gt;parity report&lt;/a&gt; compares upstream code revision &lt;code&gt;c70274273f489abc73832b2b98584a8483b6b203&lt;/code&gt; with the MLX port on CPU, using identical inputs. It separates codec behavior, per-level generator logits and completed greedy outputs.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;TinyStories&lt;/th&gt;
&lt;th&gt;Sudoku-easy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Largest compared fp32 logit difference&lt;/td&gt;
&lt;td&gt;0.100&lt;/td&gt;
&lt;td&gt;0.0000725&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Composed exact-fp64 logit difference&lt;/td&gt;
&lt;td&gt;approximately 3.22e-10&lt;/td&gt;
&lt;td&gt;approximately 1.18e-13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Greedy fixture outputs identical&lt;/td&gt;
&lt;td&gt;8/8&lt;/td&gt;
&lt;td&gt;64/64&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;TinyStories logits reach magnitudes around 853. Its tied distance head subtracts large squared-codebook terms, amplifying small upstream rounding differences. Upstream PyTorch's own fp32 result differs from the exact-fp64 reference by about 0.071. An absolute &lt;code&gt;1e-4&lt;/code&gt; threshold alone would therefore give a misleading diagnosis.&lt;/p&gt;

&lt;p&gt;The float64 row is a diagnostic computation. Upstream norms and positional tables that otherwise round internally are recomputed in float64. On the MLX side, composed operations replace primitives that do not preserve full float64 accuracy in this test, with selected functions evaluated on the host. Production inference remains float32. This supports agreement of the tested computations; it does not establish production bitwise identity.&lt;/p&gt;

&lt;p&gt;TinyStories codec code agreement is 97.4–100% per level on four input texts. Each first divergence is a near-tie in quantizer distance; changing that code changes later residuals. In the separate generation check, eight greedy fixtures have identical codes and text.&lt;/p&gt;

&lt;p&gt;The broader Sudoku evaluation provides another check: MLX solves 1,988, 1,926 and 1,724 of 2,000 published easy, medium and hard boards, respectively. Upstream PyTorch CPU and MLX produce identical outputs on all 2,000 easy boards. The full medium/hard sets were not also rerun in upstream PyTorch. These are results of the released upstream-trained models, not NCM-MLX's smaller independently trained generators.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measuring latency
&lt;/h2&gt;

&lt;p&gt;On an Apple M5 Pro with 64 GB memory, the recorded eager TinyStories batch-one median is 39.8 ms on Metal versus 280 ms for upstream PyTorch on CPU. Both complete a 128-position sample, including decoder work and a host copy. MLX uses three warmups and twenty timed iterations; PyTorch uses two warmups and ten iterations. Desktop GPU activity was present. The &lt;a href="https://janmus1c.github.io/ncm-mlx/official-checkpoints/" rel="noopener noreferrer"&gt;methodology&lt;/a&gt; records hardware, precision and contention.&lt;/p&gt;

&lt;p&gt;This compares a working GPU port with a CPU reference, not two equally optimized accelerator implementations. The tested PyTorch MPS path rejects required non-divisible area interpolation. The timing demonstrates a useful execution option on that Mac; it does not measure an architecture-only speedup or promise another Mac's latency.&lt;/p&gt;

&lt;p&gt;Later repository work adds prefix caching and opt-in compilation; these options are not part of the pinned 0.2.0 installation above. Under the released attention contract, prefix tokens attend only to themselves and their positions restart consistently. Their per-layer keys and values can be computed once per generation call. The cache belongs to that call, so different requests cannot reuse stale prefix state.&lt;/p&gt;

&lt;p&gt;For Sudoku's 91-token prefix, recorded batch-16 caching improves median latency from 207.8 to 65.4 ms in one busy-desktop comparison. At batch one, caching alone is slower: 23.4 versus 20.5 ms. TinyStories has only a one-token prefix, leaving little reusable work. Compilation is opt-in and incurs first-call tracing costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the port
&lt;/h2&gt;

&lt;p&gt;The released port provides a reference for trying NCM, examining refinement and investigating inference changes on Apple Silicon. The conversion rules and parity fixtures make those changes easier to check against the released computation.&lt;/p&gt;

&lt;p&gt;Separate, preliminary TinyStories training experiments investigate smaller independently trained models. They do not inherit the released checkpoints' quality, and their ongoing comparisons are not a new capability claim here. Neither track establishes general-purpose chat or instruction following.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/janmus1c/ncm-mlx" rel="noopener noreferrer"&gt;source and reproduction instructions&lt;/a&gt; and &lt;a href="https://huggingface.co/collections/janmusic/next-concept-modeling-in-mlx-6ae3b295f2ed5265df0906bf" rel="noopener noreferrer"&gt;converted-model collection&lt;/a&gt; provide the next steps. Cite the original paper for NCM and the &lt;a href="https://doi.org/10.5281/zenodo.23781234" rel="noopener noreferrer"&gt;versioned software record&lt;/a&gt; for this implementation. Both codebases use MIT licensing; the converted models retain the upstream license and attribution.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI assistance disclosure: OpenAI Codex assisted with implementation, experiment analysis and drafting. It is not an author; this assistance does not constitute independent verification or human review.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mlx</category>
      <category>opensource</category>
      <category>python</category>
      <category>machinelearning</category>
    </item>
  </channel>
</rss>
