YAML Metadata Warning:empty or missing yaml metadata in repo card

Check out the documentation for more information.

TensorPEFT


license: apache-2.0 library_name: tensorpeft tags: - peft - fine-tuning - tensor-decomposition - lora-alternative

Parameter-efficient fine-tuning with tensor gradient-pursuit adapters. TensorPEFT is a drop-in alternative to LoRA for Hugging Face models, designed for very small trainable-parameter budgets.

LoRA gives every weight matrix its own low-rank update. TensorPEFT instead stacks all equal-shaped matrices of the network into one tensor, for example layers x {q,k,v,o} x d_in x d_out, and learns a few rank-one CP atoms of that tensor:

dW = sum_i  a_i  u_i(layer) o u_i(role) o u_i(in) o u_i(out)

Each atom couples every layer and projection while costing only L + P + d_in + d_out + 1 parameters. The atoms are not random. They are the leading rank-one CP components of the negative gradient tensor (higher-order power iteration with deflation), computed from one gradient probe before training. The coefficients start at zero, so the model is unchanged at step 0, and every atom starts on a steepest-descent direction. After that, training is ordinary: any optimiser and the HF Trainer both work.

Install

pip install tensorpeft          # once published; until then: pip install .

It requires torch>=2.1, transformers>=4.40, safetensors and huggingface_hub.

Quick start

from transformers import AutoModelForCausalLM
from tensorpeft import TensorPEFTConfig, get_tensorpeft_model, TensorPEFTModel

base = AutoModelForCausalLM.from_pretrained("gpt2")
model = get_tensorpeft_model(base, TensorPEFTConfig(atoms_per_group=4))

model.init_atoms(train_dataloader, n_batches=4)   # the one step LoRA does not have
model.print_trainable_parameters()                # trainable params: 37,036 || ... || 0.0298%

# train as usual: AdamW, lr ~3e-3, short warm-up, weight_decay 0  (or TensorPEFTTrainer below)

model.save_pretrained("my-adapter")               # adapter_config.json + adapter_model.safetensors
model.push_to_hub("your-name/my-adapter")

base = AutoModelForCausalLM.from_pretrained("gpt2")
model = TensorPEFTModel.from_pretrained(base, "your-name/my-adapter")   # local dir or Hub id
merged = model.merge_and_unload()                 # plain HF model, zero inference overhead

train_dataloader can yield dicts (input_ids, optional attention_mask / labels) or plain input-id tensors. For causal LMs, labels default to the inputs.

With the Hugging Face Trainer

from tensorpeft import TensorPEFTTrainer
trainer = TensorPEFTTrainer(model=model, args=training_args, train_dataset=ds, data_collator=collator)
trainer.train()             # checkpoints contain only the adapter

TensorPEFTTrainer is the stock Trainer except that it saves adapters instead of full model weights. A complete script is in examples/train_with_trainer.py.

Configuration

TensorPEFTConfig field default meaning
atoms_per_group 4 rank-one atoms per tensor group; this sets the budget
target_modules "all-linear" or a list of name suffixes, e.g. ["q_proj", "v_proj"] ("c_attn.q" for GPT-2)
exclude_modules lm_head, embeddings never adapted
split_fused_qkv True split GPT-2's fused c_attn into q, k and v
init_batches 4 batches used by init_atoms
hopm_iters 30 power-iteration sweeps per atom
alpha_scale 33.3 coefficient reparametrisation so that one learning rate (~3e-3) fits coefficients and factors

Budget. Groups are formed automatically by matrix shape. GPT-2 small has three groups: {q,k,v,o} (12x4x768x768), MLP-in (12x768x3072) and MLP-out (12x3072x768). One atom per group is 9,259 parameters, four per group is 37,036, and six per group is 55,554. print_trainable_parameters() shows the exact count for any model.

Recipe. Use AdamW at lr 3e-3 with about 10 warm-up steps, then a constant schedule and weight decay 0. Use all atoms from the start: adding atoms gradually during training was clearly worse in our tests.

Results

Setup: GPT-2 124M (English base) fine-tuned on Bengali news (3M tokens), 300 AdamW steps, batch 8 x 128 tokens. Validation cross-entropy is in nats/token, lower is better; the base model scores 1.708. All methods saw identical data, and baseline learning rates follow their papers.

Three seeds, best baseline per budget:

budget TensorPEFT best baseline difference seeds won p (paired t)
~9k params 1.3245 ± 0.004 prompt tuning: 1.3684 ± 0.010 -0.044 3/3 0.008
~37k 1.2558 ± 0.010 LayerNorm tuning (38.4k): 1.2504 ± 0.003 +0.005 1/3 0.37 (tie)
~55k 1.2368 ± 0.017 (IA)³: 1.2880 ± 0.003 -0.051 3/3 0.026

All baselines at each budget (seed 0):

budget TensorPEFT LoRA QLoRA VeRA (IA)³ prompt tuning LayerNorm tuning BitFit
~1.5k 1.461 - - - - 1.493 (768 params) 1.600 -
~9k 1.327 1.430 - - 1.432 1.370 1.407 -
~37k 1.255 1.318 1.335 1.393 (24.6k) - 1.327 1.253 -
~55k 1.231 - - - 1.289 - - -
~83k - - - - - - - 1.326

TensorPEFT budgets map to configs as follows: ~1.5k is one atom on the attention tensor only (target_modules=["c_attn.q", "c_attn.k", "c_attn.v", "attn.c_proj"], atoms_per_group=1), ~9k is atoms_per_group=1, ~37k is 4 and ~55k is 6. A dash means the method cannot be configured at that budget on GPT-2, or was not run there. LoRA's smallest all-layer configuration is r=1 on q,v (37k parameters). At 9k it is LoRA r=1 on q,v of the last 3 layers.

What this does and does not show. TensorPEFT beats every baseline from about 1.5k to about 9k parameters and at about 55k, and ties LayerNorm tuning at about 37k. With larger budgets (≥ 115k) LoRA was better in our tests. The evidence covers one base model, one task and short training. Results on other models and downstream tasks are not yet available.

examples/reproduce_gpt2_bengali.py reruns these benchmarks with this package (--atoms-per-group 1 | 4 | 6). The data comes from the Bengali news corpus at github.com/soham96/Bengali_news_classifier, tokenised with the GPT-2 tokenizer.

Supported models

Any model whose transformer blocks use nn.Linear or GPT-2-style Conv1D layers inside numbered blocks (...layers.N... / ...h.N...). Tested on GPT-2, Llama (GQA), Qwen2, Mistral, Phi and Gemma configurations. Fused projections other than GPT-2's c_attn are treated as a single matrix.

Limitations (v0.1)

  • GPU support is untested. The code is device- and dtype-agnostic (adapter parameters in fp32, bf16/fp16 bases supported), but all tests and benchmarks so far ran on CPU.
  • Probe memory. init_atoms needs one backward pass with gradients on the targeted weights, plus an fp32 copy of each group's gradient tensor. That is roughly 2x the size of the targeted weights, temporarily. Use target_modules to limit it for large models.
  • Quantised bases (bitsandbytes 4/8-bit) are not supported yet, because the probe needs weight gradients.
  • No checkpoint resume. Trainer resume-from-checkpoint is not implemented; reload with TensorPEFTModel.from_pretrained(..., is_trainable=True).

Tests

pip install .[test]
pytest tests

License

Apache-2.0. Copyright 2026 Angshul Majumdar.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Space using Angshul/TensorPEFT 1