Skip to content

apr run: inference fails on tied-embedding .apr models (lm_head 0-byte placeholder not tied to embed_tokens) #2309

Description

@noahgift

Summary

apr run fails at the final lm_head matmul on tied-embedding .apr models with:

error: Inference failed: Invalid shape: matmul weight has EMPTY data buffer
(in_dim=896, out_dim=151936, qtype=0); likely a MoE per-expert tensor was
registered with len-0 data — see aprender#1789

The error's MoE hypothesis (#1789, closed, was Qwen3-30B-MoE) is a red herring — this model is not MoE. The real cause is tied word embeddings.

Repro

apr run /home/noah/models/qwen2.5-coder-0.5b-instruct.apr --prompt "What is 2+2?" --max-tokens 16
# fails on --no-gpu, --backend cpu, AND wgpu — backend-independent

Diagnosis

apr tensors shows the lm_head is a 0-byte placeholder, while embed_tokens holds the real data:

│ lm_head.weight            │ [151936, 896] │ f32  │ 0 B      │   <-- empty
│ model.embed_tokens.weight │ [151936, 896] │ bf16 │ 259.7 MB │   <-- real data

Qwen2.5-0.5B uses tie_word_embeddings=true: lm_head == embed_tokens (transposed for the output projection). The .apr file correctly stores lm_head as a shape-only placeholder, but the loader registers the empty lm_head for the output matmul instead of tying it to embed_tokens.weight.

apr inspect / apr qa PASS (checksum valid, 291 tensors pass PMAT-235 contracts) — the file is well-formed; the loader is at fault.

Scope

  • Fails: tied-embedding models (small Qwen: 0.5B; any model with a 0-byte lm_head placeholder).
  • Works: models with a real/untied lm_head — e.g. qwen2.5-coder-1.5b-instruct-q4k publishes "4" correctly.

Regression status

NOT a v0.60.0 regression — the 0.57.0 dev build (/mnt/nvme-raid0/targets/aprender/release/apr) fails identically. Pre-existing (≥0.57.0). Surfaced by post-release dogfooding of the v0.60.0 crates.io binary.

Proposed fix

In the quantized-model loader (OwnedQuantizedModel::from_apr / the output-projection wiring): when lm_head.weight has a 0-byte data buffer AND embed_tokens.weight exists with a matching [vocab, hidden] shape, tie the output projection to embed_tokens.weight (transposed) rather than registering the empty buffer. Add a falsification test that apr run on a tied-embedding fixture produces a non-empty decode (RED before fix, GREEN after), per the contract-ratchet doctrine.

Also: fix the misleading error message to name the tied-embedding case, not just MoE #1789.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions