Local AI models, torn down, modified, and tested.
I'm a software engineer (professionally .NET, personally omni-curious) who's been trying out local LLMs, partially in hopes of finding a way to reduce my cloud usage, and partially in order to learn more about how they work by modifying them. You can learn a lot about how so many things function by disassembling them, and LLMs are all the rage these days, which makes them a great candidate that won't end up in the landfill when I do something wrong.
The draw
I've been inspired by many people who spend their time and prodigious compute on fine-tuning, quantizing, and experimenting with models, and all the open source software they publish. I have no pretensions of creating models that can compete or eclipse those released by the big organizations, but I can attempt new experiments and share what I've come across for others to read.
Another draw is the feel of hanging out at the raw edge of technological progress in the community driven space of local LLMs. It resembles to me past technology boom periods where anyone could become a wizard in a new technology and people cobbled together enterprise hardware to host free websites or services. For my part, I'm doing all this on a base model Mac mini, which admittedly is uniquely capable for LLMs, but still nothing on the scale of the compute available to frontier companies or even research groups. The ability to perform all this on my own device, no signups, no fees, and no worrying that a tweak I've tried will cost me wasted money on a rented server. All this runs in the background and I can pivot my tasks when I want.
Sections
Intervention
Exploring what happens when you modify models. Layers duplicated, pruned, damped, and recombined, then measured to see how the resulting model fares.
The model layer experiments are inspired by the work of David Ng, and while I haven't had the same amount of success on leaderboards, I have been expanding on his method and tackling the ever present difficulty of evaluating model performance.
MiniCPM5 — the lightweight prototype
- arch
- dense · 1B
- runtime
- MLX · f16
- op
- layer dup/prune · damping
The model didn't produce an unambiguous success, but the instrumentation was built here, and one empirical regularity held up across every later specimen.
Nanbeige — the overthinking curiosity
- arch
- dense · 3B
- runtime
- llama.cpp · Q8_0
- op
- layer dup/swap
The one specimen where inference-time surgery measurably helped, albeit in a roundabout way.
Granite — revisited and overturned
- arch
- dense · 3B
- runtime
- MLX · 4-bit
- op
- dup/prune · Σα damping
This page showed me just how far my instrumentation had come, and it required more than just a few edits to address. Both the original and addendum update coexist to demonstrate how a rigorous approach matters. And, of course, the revised result isn't kind to the hypothesis of altering the granite model.
Gemma 4 E4B — a unique design
- arch
- dense · shared-KV
- runtime
- llama.cpp · IQ4_XS
- op
- dup/prune/swap
The original dissection and published page, and one of the larger models to receive an extensive sweep of its layers. This model also demonstrated the fragility of the evaluator and the necessity of interpreting the results correctly. The lenient grader handed back a tidy list of winners, but the strict regrade culled most of them. This model also served as a proof of concept as RYS layer edits don't play well with models structured like this. Unlike the dense isotropic models where each layer just interfaces with its neighbors, this model required some careful editing and remapping to work correctly, and certain layers remain off limits.
Gemma 4 31B — the heavyweight
- arch
- dense · 31B
- runtime
- llama.cpp · IQ2_M
- op
- dup/prune sweep · 176 cfg
This is a report of a 176-config dup/prune sweep of a flagship 31B model squeezed into 16 GB at IQ2_M, and not one surgery won. This model stressed my machine and my patience, but given its size I had hoped to finally see a notable result. The original method suggests that a model with more layers develops a real language-independent section of layers that could be duplicated to improve reasoning without disturbing its processing. These findings suggest minimal to no improvement.
MoE - Experts, not layers
- arch
- MoE · multi-model
- op
- expert removal · REAP/SEC
Every other experiment treats a model as a stack of layers, but there may be a different lever to try on mixture-of-experts models. This isn't a new idea either, and it has more institutional support in the form of Cerebras REAP, but I'm curious what happens when you take it further than they suggest.
HRM — a promising idea, not model
- arch
- recurrent · 1B
- runtime
- MLX · bf16
- op
- loop depth · recurrence
- status
- draft
A Hierarchical Recurrent Model sounds like the perfect experimentation platform for me. After all, my whole hypothesis is increasing reasoning depth through looping layers rather than just making a bigger model or training it. This is a novel class of transformer LLMs that's not ready for prime time yet, but that didn't stop me from exploring the exact promise of looped transformers - a built in system to multiply the number of passes the token takes through layers. The first overnight test delivered outstanding and exciting results, but as is a recurring theme here, upon further inspection and more rigorous testing, it turned out to be a fluke.
Observation
The other side of layer edits is the hidden layer scanning that led to the hypothesis behind RYS. David Ng showed cosine similarity charts for paired phrases, layer by layer, that illustrated the potential of a “reasoning zone.” It's a powerful visualization that demonstrates how the model transforms all inputs into intermediate values that don't correspond to our language or understanding, and I enjoy seeing these visualizations for each of the models.
Internal Geometry - Visualizing nine models' hidden states
- arch
- 9 models
- op
- centered cosine · delta
The RYS literature says smaller models don't develop a convergent reasoning zone in the middle layers. I scanned them anyway: the mid-stack zone shows up fine — what never shows up is any connection to RYS results, since the winning configs sat on layers the scans didn't flag. Still some interesting standouts, namely in the Gemma 4 family.
The Vivarium - Gemma 4 12B on day zero
- arch
- dense · 12B
- runtime
- MLX · mxfp4
- op
- hidden-state scan
This brand-new dense Gemma model dropped right as I was refining the MLX hidden-state extraction script, and it seemed like a perfect opportunity to crack the model open before even a single fine-tune came out. The scan was interesting, but I took it further by investigating per-token hidden states - comparing two sentences that differ by barely a word but whose meanings diverge widely. This created a visual demonstration of where in the model a token takes on meaning, rather than being a number corresponding to a dictionary entry.
Method
The recurring pattern in all the model sweeps I've performed is just how crucial the methodology of evaluating the model is. It comes down to crafting or importing the right question set to choose probes, refining a grading pattern to reduce false positives and false negatives, setting up the inference runtime to restrict the model from CoT reasoning, tweaking the prompt to encourage it to respond with just the answer, playing with the chat template, etc. - and then doing all that over again on a new model that plays by different rules.
On top of that, when you're experimenting with LLMs on consumer hardware, every tweak that makes it run faster is worth it, from benchmarking runtimes to making a custom prompt-batching pipeline to reduce overhead. Even step one - scanning the model to assess roughly what layers will be actionable - can be optimized.
The hidden-state extraction script was written for PyTorch with raw fp16 transformers, but I needed it to work with quantized models. And then I investigated whether MLX could perform the same task, since it also operates on raw transformers rather than a packaged GGUF. Editing layers on the fly in MLX is natively supported, but digging into this functionality offered the opportunity to try out layer damping, which turned out to be a useful layer-modification path, less disruptive than pruning or duplicating. It also set up future experiments in related tangents - activation steering/injection, sparse autoencoders, and diffusion models.
The terse Methods & references page holds the shared vocabulary; the vivisection & vivarium overview-methodology pages will join it.
Anatomy of a sweep
Running the surgery isn't difficult once you establish the mechanics of it; extracting and evaluating the resulting model's response is where it turns into work. Three traps do most of the damage: a lenient grader inflates partial credit, the model can echo the answer straight back, and format-compliance passes for capability if you don't check. So before spending compute I profile how much headroom the baseline even leaves to measure, then calibrate the grading channel against it.
MLX is the fastest local runtime*
The asterisk is the whole point: 1B dense model, batched decode only, M4 Mini, custom generation loop, raw tok/s - every qualifier is on the label. The more interesting result, outside that narrow use case, is that MLX and llama.cpp are pretty evenly matched, so there isn't really a headline about which runtime is better.
Systems
This is driven by my relatively recent dive into using frontier LLMs for development — the last time I really tried them, it was more a party trick than an actual product. Claude Code has been instrumental in diminishing the setup friction and distractions so I can focus on what I set out to do, but it's also a fantastic pair programming partner for these novel tasks. In fact, the very investigations linked on this page are downstream results from my initial LLM-related project of wanting a cross platform memory system that didn't require so much manual file juggling or reminders.
These projects concern agents and LLM memory systems, and serve more as experimentation and personal tooling than as polished products.
duologue - one model, two agents
Duologue is more in the spirit of the model experimentation in the previous sections than a functional use for agents, but it did submerge me into the tricky world of context management and orchestration. It started with two models in RAM concurrently. I can't run two Qwen-27Bs at once, though, so the trick became feeding a single model a carefully crafted history that makes it believe it's talking to someone else. That resolved the mechanistic aspect. The fun part ended up being setting up various scenarios for it: first to see whether it recognizes the other agent as AI, and later whether the two agents could communicate under strict restrictions on their messages to each other.
mem0-processor
The project that kicked off everything else here. I wanted a persistent memory store kept current by LLMs, but the options were flawed: claude.ai's user memories are occasionally wrong, Claude Code has the model write its own unreliably, the two don't even talk to each other, and everything I found leaned on paid Claude to generate the memories. So I built a pipeline that uses free open-weight models to summarize my sessions and upload them to mem0, keeping close control over the transcript and prompt. Trusting those local summaries meant scoring the models to see which were any good, and that scoring rig quietly became a real evaluation harness: 119 models scored out of a 145-model registry, 62 clearing the quality bar.
Teaching an agent to read its own logs
What do you do when you have 1200 session summaries from hundreds of different local models sitting on your storage drive from testing the mem0 pipeline? I couldn't let that go to waste, and I'd been meaning to optimize my CLAUDE.md empirically from past experience. The summaries are meant to be small enough to parse easily, but cumulatively they add up to a corpus too large for any model to process at once. Through several layers of aggregation, I used Claude to distill them into meaningful observations that measurably improved my system prompt and memory system, to the point where starting a new session can feel frictionless.
How I work
As evidenced by the variety of topics above, my attention jumps around when unbound, but the one thing I'm strict on is keeping my results in check. I don't claim a model that got better. I claim a better measurement layer under it: seed discipline, paired effect sizes, and a strict regrade that culls the wins that don't replicate. I direct and decide; large models read code, run the statistics, and draft. The judgment about what's true is mine. I work collaboratively with Claude to accelerate my workflow and to explore novel areas I wouldn't have the faintest clue where to start.
Traceability
Every number on this site traces back to a file in a repo: the config hash lives in the filename, the raw per-probe results are kept as JSONL, and the strict score always leads. I won't publish a comparison that isn't backed by an artifact you could open and re-run yourself.
Transparency
Many of the headline findings on this page were wrong, and were wrong due to incorrect interpretation or configuration. I want to ensure that I don't overstate my results, and that I provide the full story of how I arrived at them rather than cherry-picking or stripping out the failures.
Efficiency
I appreciate efficiency in software engineering, both from a performance aspect and the perspective of selecting the right framework to match the needs of a project. This is reflected in this site: it's static HTML, ~1.5 KB of JS, slim SVG figures, no framework, one cookieless tracker. It is everything it needs to be and nothing more, and the simple structure has the added benefit of being easily parseable by screen readers or LLMs, and treats accessibility as a priority, not an afterthought.