Read Light-PEFT as an academic early-pruning research release: learn why frozen-backbone forward cost remains, how masks become physical structured pruning, and how PEFT modules and ranks shrink before a longer fine-tuning run.
Personalize this lesson
Adapt explanations and teaching visuals to your background and preferred voice.
Parameter-efficient fine-tuning (PEFT) freezes most backbone weights and learns a small update. That saves optimizer state and gradient storage, but it doesn't make the frozen network disappear from every training step. A frozen layer still computes activations, and those activations still carry information needed by backward.
Light-PEFT asks a sharper question: after a task has shown which heads, feed-forward dimensions, adapter modules, and adapter ranks it uses, why keep paying for the rest? The paper's answer is a short task-specific estimation phase followed by physical structured pruning and a longer fine-tuning phase.[1]
This chapter reads the pinned research release rather than treating Light-PEFT as a maintained product. It connects the equations to NLU/estimate.py, NLU/train.py, and the small PEFT fork, then separates paper evidence from what a modern model-serving or reinforcement-learning system would still need to prove.
Suppose a Transformer layer receives hidden states X. With ordinary LoRA, a frozen weight W receives a low-rank update:
where A maps from the hidden dimension to rank r, B maps back, and s is a scale. Only A and B need optimizer updates. The layer still evaluates XW' for every token.
The phrase frozen describes parameter updates, not execution. A training step still has this shape:
PEFT removes a large optimizer and gradient burden. It doesn't automatically remove backbone FLOPs, backbone weight reads, or activation memory. The paper measures this directly: as OPT grows, forward time and peak memory rise even though LoRA avoids most backbone parameter gradients.[1]
Use one layer with hidden size d = 8, sequence length S = 4, and a feed-forward width of 32. A rough dense projection count for one token is proportional to:
| Work item | Shape or count | Why it remains under ordinary PEFT |
|---|---|---|
| Query, key, value projections | 3 × 8 × 8 = 192 multiply-add pairs | Frozen matrices still produce attention inputs |
| Attention output projection | 8 × 8 = 64 pairs | Residual output still needs the projection |
| FFN up projection | 8 × 32 = 256 pairs | Every intermediate coordinate is computed |
| FFN down projection | 32 × 8 = 256 pairs | Every coordinate can affect residual output |
| LoRA update at one target | 8 × r + r × 8 pairs | Small when r is small, but additive |
The adapter may add only a small number of trainable parameters. The frozen 768 projection pairs per token still run. For S = 4, that is 3,072 rough projection pairs before counting softmax, normalization, activation, and memory movement.
If a task never uses one attention head, or if many FFN coordinates carry little task signal, PEFT alone has no reason to skip them. A task-specific structured mask can make those coordinates absent from the second run.
Quantization stores weights in fewer bits. It can reduce load and optimizer memory, but training kernels may dequantize values for computation. Quantization changes bytes per value; structured pruning changes how many rows, columns, heads, or dimensions exist. Light-PEFT studies the second lever. Its method can be combined with quantization in principle, but the paper's experiments don't establish a QLoRA combination.[1]
The source at commit 7b10c22026fe6fa283d127353a50d6ed7176a8aa is a small academic code release for the Findings of ACL 2024 paper. The paper authors are Naibin Gu, Peng Fu, Xiyu Liu, Bowen Shen, Zheng Lin, and Weiping Wang. Their listed affiliations are the Institute of Information Engineering, Chinese Academy of Sciences, and the School of Cyber Security, University of Chinese Academy of Sciences.[1] The paper and repository don't name a company behind the project; primary ownership evidence points to those academic institutions and the gccnlp repository account.
Paper authors and code contributors are related evidence, not interchangeable labels. The pinned Git history has six commits, all with commit identity gccnlp and email domain iie.ac.cn; it has no tags or GitHub releases. The GitHub contributors page is a separate activity view and shouldn't be used to infer that every paper author committed every file.[2][3]
License wording needs the same care. The repository root has no repository-wide license file. The bundled peft/ subtree contains an Apache-2.0 LICENSE, and its setup metadata identifies that package as a PEFT fork. It's correct to call that subtree Apache-2.0 licensed. It's not correct to call the full Light-PEFT repository Apache licensed.[2][4]
This distinction affects reuse. A research reader can inspect the code, paper, experiment settings, and history. A production team still needs a license review for every file, model checkpoint terms, dependency terms, and a maintenance plan. There is no service-level guarantee, release cadence, or current model catalog encoded by this checkout.
Repository maturity is equally narrow. There is no GitHub Actions workflow and no focused test suite for the Light-PEFT pruning path. The bundled peft/tests files exercise inherited configuration and adapter behavior, not the two-phase mask-to-surgery contract. Static Python compilation succeeds at the pinned commit, but that only proves files parse. It doesn't validate mask learning, tensor slicing, checkpoint restoration, or reported performance.[2]
The checkout is intentionally direct. It doesn't hide the two-phase boundary behind a package command.
| Path | Role | Evidence to inspect |
|---|---|---|
README.md | Install and experiment entry points | estimate.sh, train.sh, and vanilla_train.sh |
scripts/estimate.sh | First phase command | dataset, model, rank, mask trainer, output directory |
scripts/train.sh | Second phase command | pruning ratios, peft_dir, and continuation flags |
scripts/vanilla_train.sh | Baseline comparison | same dataset family without early pruning |
NLU/estimate.py | Mask and importance recorder | callback, .npy, and .pkl writes |
NLU/train.py | Longer continued run | reads pruned model and adapter artifacts |
NLU/get_trainer.py | Mode selection | early search versus draw-and-training |
NLU/trainer_base.py | Loss and optimizer behavior | mask penalty and importance collection |
NLU/nlu_utils.py | Physical pruning helpers | head, FFN, module, and rank surgery |
models/hf_roberta/modeling_roberta.py | Backbone fork | mask multiplication and linear slicing |
peft/src/peft/ | PEFT implementation | LoRA and Adapter wrappers plus subtree license |
requirements.txt | Environment pins | Torch 2.0.1, Transformers 4.31.0, NumPy 1.25.1 |
The package is a snapshot, not a general adapter framework. The custom model map currently registers RoBERTa sequence-classification and multiple-choice paths. The paper evaluates LoRA and Adapter on RoBERTa-Large, OPT-1.3B, and OPT-6.7B, but the repository's runnable NLU path is much narrower than that sentence might suggest.[1][2]
The release pins torch==2.0.1, transformers==4.31.0, datasets==2.13.0, numpy==1.25.1, scikit_learn==1.3.0, fairscale==0.4.13, and fsspec==2023.6.0. These versions explain how the original code was tested. They don't promise compatibility with current Transformers model classes, CUDA drivers, or a newer PEFT API.
An environment receipt should include:
pip freeze or lock export;Without those fields, a speed or accuracy number is hard to reproduce. A green install log doesn't prove that the second command loaded the intended reduced shape.
Light-PEFT separates estimation from physical pruning. During the first phase, masks and importance scores are trainable or recorded alongside PEFT updates. After a short prefix of training, the code selects survivors, slices tensors, writes records, and starts another trainer with those smaller tensors.
Let t be the full training step count and t′ the estimation prefix. The paper uses early prefixes around 5% to 10% for its NLU studies and reports that extending estimation beyond about 6.8% didn't improve BoolQ in its appendix experiment.[1]
The lifecycle is:
peft_dir.draw_and_training=True.The mask is a score during phase one. A pruned dimension is a missing row or column during phase two. That change is why the second command must use a compatible checkpoint and adapter state.
The first method targets the frozen Transformer. It has two trainable mask families: one scalar per attention head and one scalar per FFN intermediate dimension.
Let layer i have N_H heads. A head output is multiplied by scalar m_A^(i). For one head:
When m_A is near zero, the head contributes little to the task loss under this early trajectory. The mask starts at one, so initial behavior matches the unmasked model. Gradient descent can move individual values, while an L1 term makes small values attractive.
The repository implements this in models/hf_roberta/modeling_roberta.py. When config.mask_training is true, each attention block creates early_attn_mask shaped like (1, heads, 1, 1) and multiplies attention probabilities by it. At pruning time prune_heads builds an index of retained head coordinates and slices query, key, value, and output linear layers.[2]
That slice is structured. It removes a whole head's feature range, not random individual weights. Tensor cores and framework kernels can see a smaller dense matrix.
An FFN block has an intermediate activation:
followed by a mask and the down projection:
Here m_F has one scalar per intermediate coordinate. In the source, early_ffn_mask is shaped like (1, intermediate_size). prune_inter_neurons slices intermediate.dense output rows and output.dense input columns using the surviving indices.[2]
Removing an FFN coordinate requires both sides of the bottleneck to agree. Slicing only fc1 leaves fc2 expecting the old width. Slicing only fc2 leaves an activation with a mismatched width. The helper changes both.
The mask trainer adds:
trainer_base.py loops through attention and layer modules, sums the absolute values, and multiplies by l1_loss_coef. The shell example sets l1_loss_coef=1e-4. The paper's appendix reports λ_A = λ_F = 1 × 10^-4 for its main experiments.[1][2]
L1 doesn't decide an exact number of heads or neurons by itself. The pruning ratio and quantile decide that later. L1 makes the score distribution easier to rank, then the requested ratio sets a capacity budget.
For attention heads, the helper defaults to layer-wise pruning. It computes a quantile separately within each layer, then prunes values at or below that layer's threshold. Every layer therefore loses roughly the requested fraction, subject to ties.
For FFN dimensions, train.sh passes --ffn_pruning_method "global". The helper accepts pruning_method="global", sets quantile_axis=None, and computes one threshold across all layers and dimensions. A layer with uniformly useful coordinates can keep more dimensions while another layer loses more.
This difference is intentional. Heads are repeated semantic routes within each layer. FFN dimensions are a large shared pool where global ranking can spend the budget unevenly.
get_early_model uses the saved mask arrays by default. With random_pruning=True, it creates seeded random scores and uses them in place of recorded values. That branch can test whether a claimed gain depends on task evidence rather than any structured shape reduction.
Random pruning still changes tensor shapes. It isn't a no-op baseline. Compare it with the same seed policy, ratio, hardware, and total time before attributing performance to mask learning.
The second method sees adapter capacity at two scales:
Module removal can cut many small operations. Rank removal can retain a module while reducing its internal width. The paper reports both because increasing the number of structured modules can affect forward and backward time more than increasing the rank of one module under a fixed parameter budget.[1]
For LoRA, the repository and paper compare the adapter output to the frozen weight output:
For an Adapter inserted after a sub-layer with output h:
An output ratio near zero says the module changed this task's activations little during the observed prefix. The code collects pos_importance_scores, averages the processed history, and writes peft_module_records.pkl.[1][2]
The LoRA estimate temporarily places modules on all targeted weights so it can compare positions. This can cost more during the short prefix than a usual LoRA run. The paper's argument is that a small search cost can pay for a longer reduced run.
The helper prune_peft_modules takes module scores, computes a global quantile, and returns adapter parameter keys for surviving modules. get_early_peft_model filters the adapter state dict to those keys, sets reserved_modules, and enables prune_module so future wrapping skips removed positions.
For parameter W_{i,j} in either down or up projection, the paper writes the first-order term as:
The rank score sums terms for one column of W_down and the matching row of W_up. This treats a rank coordinate as a unit. Removing only one side would break the low-rank product, so both matrices are sliced. The paper's displayed equation doesn't put absolute-value bars around this product.
The repository makes a more specific implementation choice. In MaskTrainer.training_step, each PEFT module computes weight * grad for its down and up matrices, averages along the complementary axis, takes absolute values of those means, stacks the two vectors, and sums them into importance_scores. The callback averages the later half of the history, replaces NaN and infinity with zero, multiplies by the current mask when present, and writes peft_mask_records.pkl.[2]
The source's prune_peft_rank then computes a quantile across rank scores. For LoRA it slices lora_A rows and lora_B columns. If every rank is selected for removal, the module gets rank zero and its adapter is disabled. The same pattern exists for the bottleneck Adapter fork.[2]
The helper accepts peft_pruning_method. A layer-wise choice computes a threshold inside each module. A global choice compares rank coordinates across all matching adapter modules. The shell defaults to global for FFN dimensions but leaves PEFT method explicit, so an experiment receipt should record both choices.
Global rank pruning can concentrate capacity in a few modules. That may be useful for one task, but it makes a shared serving policy less predictable. Layer-wise rank pruning keeps every module alive at some width but can leave low-value modules with tiny ranks.
Use four attention heads in one layer. After the estimation prefix, suppose the mask values are:
| Head | 1 | 2 | 3 | 4 |
|---|---|---|---|---|
m_A | 0.91 | 0.08 | 0.72 | 0.54 |
Request an attention pruning ratio of 0.25. With NumPy's default linear interpolation, the layer-wise 25th percentile is 0.425 for this tiny list. The source keeps values strictly above that threshold, so head 2 is removed and heads 1, 3, and 4 survive. Query, key, value, and output slices now use three head ranges instead of four.
Now use five FFN coordinates across two layers:
| Coordinate | L0-n1 | L0-n2 | L0-n3 | L1-n1 | L1-n2 |
|---|---|---|---|---|---|
m_F | 0.07 | 0.63 | 0.42 | 0.15 | 0.31 |
For a global 40% pruning ratio, sort values: 0.07, 0.15, 0.31, 0.42, 0.63. A threshold around the second value removes two low coordinates, leaving 0.31, 0.42, 0.63. Actual NumPy quantile interpolation and strict comparison decide ties, so always inspect the saved indices.
For adapter module ratios, let three LoRA insertions produce 0.04, 0.31, and 0.11. A module ratio of 0.33 removes the lowest third, likely the 0.04 module. For the surviving module, suppose rank scores are [0.02, 0.17, 0.03, 0.22] and rank pruning ratio is 0.50. Keep the two largest coordinates, ranks 2 and 4 in this local example. The adapter's down projection keeps the corresponding rows and its up projection keeps matching columns.
The arithmetic separates three decisions:
None of those steps says that a coordinate is universally unimportant. A new task, sequence distribution, adapter target set, or reward can rank it differently.
The default scripts/estimate.sh chooses one GPU, GLUE SST-2, RoBERTa-Large, LoRA rank 8, sequence length 128, batch size 32, five epochs, and max_steps=800. It passes --early_search True --use_mask_trainer True --use_peft True and writes under:
1results/sst2/roberta-large-lora-p2-first_order/The exact path depends on DATASET_NAME, model_name_or_path, adapter name, and peft_imp. The source's p2-first_order string controls how PEFT importance is recorded. It isn't a generic method name recognized by current PEFT releases.
estimate.py recordsAt on_train_begin, SuperGLUECallback creates one list per encoder layer for attention and FFN masks. It also writes init_peft_mask_records.pkl with initial PEFT mask tensors.
At each evaluation, the callback records current mask values. At train end it records one final value, stacks each layer's history, and saves:
1early_attn_mask_records.npy
2early_ffn_mask_records.npyIt also processes each PEFT module's score history. The code averages the latter half of the history, sanitizes NaN and infinity, and writes:
1peft_module_records.pkl
2peft_mask_records.pklThese files are small relative to the model, but they are part of the run's provenance. Hash them and keep the seed with the command.
train.py readsThe default scripts/train.sh points peft_dir at the estimate output and sets:
1attn_pruning_ratio=0.333333333
2ffn_pruning_ratio=0.333333333
3module_pruning_ratio=0.75
4rank_pruning_ratio=0.5
5ffn_pruning_method=globalIt passes --draw_and_training True --use_peft True. get_trainer.py calls get_early_model, which loads the two .npy arrays, chooses the final recorded step, and prunes RoBERTa heads and FFN dimensions. It then calls get_early_peft_model, which loads the adapter state and two pickle files, filters modules, slices rank matrices, wraps the reduced model, and restores the filtered adapter state.[2]
The second command isn't a resume in the optimizer sense. It constructs a new model path and a new trainer after tensor surgery. If you need an uninterrupted optimizer trajectory, this code isn't that path. It's a short search followed by continued task training on a changed architecture.
estimate.sh and train.sh can fail separately| Symptom | Likely boundary | Check |
|---|---|---|
FileNotFoundError for .npy | estimate output mismatch | print peft_dir and list files |
| adapter key mismatch | base model or target module changed | compare model name and LoRA target list |
| linear shape error | pruned config not paired with state | inspect head count and FFN width |
| all modules removed | quantile or ratio too aggressive | count survivors before wrapping |
| NaN score | unstable loss or gradient | inspect score history and learning rate |
| no speed gain | kernels or data pipeline dominate | profile forward, backward, and input work |
| slower total result | search cost omitted or too large | measure both phases from clean start |
Keep command output in a receipt. The printed "pruning complete" line proves only that control flow reached the helper. It doesn't prove shape correctness, throughput gain, or task quality.
The following standard-library example mirrors score selection, strict > thresholding, and rank pairing without importing Torch or NumPy. It doesn't emulate Transformer kernels. Its value is the invariant: every retained rank is present in both adapter projections, and every selected backbone coordinate is explicit.
1from dataclasses import dataclass
2
3def quantile(values, ratio):
4 ordered = sorted(values)
5 if not ordered:
6 raise ValueError("need at least one score")
7 position = (len(ordered) - 1) * ratio
8 low = int(position)
9 high = min(low + 1, len(ordered) - 1)
10 fraction = position - low
11 return ordered[low] + fraction * (ordered[high] - ordered[low])
12
13def keep_above(scores, ratio):
14 threshold = quantile(scores, ratio)
15 return threshold, [index for index, score in enumerate(scores) if score > threshold]
16
17@dataclass(frozen=True)
18class RankPair:
19 down_row: int
20 up_column: int
21
22heads = [0.91, 0.08, 0.72, 0.54]
23ffn = [0.07, 0.63, 0.42, 0.15, 0.31]
24rank_scores = [0.02, 0.17, 0.03, 0.22]
25
26head_threshold, kept_heads = keep_above(heads, 0.25)
27ffn_threshold, kept_ffn = keep_above(ffn, 0.40)
28rank_threshold, kept_ranks = keep_above(rank_scores, 0.50)
29rank_pairs = [RankPair(index, index) for index in kept_ranks]
30
31assert kept_heads == [0, 2, 3]
32assert kept_ffn == [1, 2, 4]
33assert [pair.down_row for pair in rank_pairs] == [1, 3]
34assert [pair.up_column for pair in rank_pairs] == [1, 3]
35
36print(f"heads threshold={head_threshold:.3f} keep={kept_heads}")
37print(f"ffn threshold={ffn_threshold:.3f} keep={kept_ffn}")
38print(f"rank threshold={rank_threshold:.3f} pairs={rank_pairs}")1heads threshold=0.425 keep=[0, 2, 3]
2ffn threshold=0.246 keep=[1, 2, 4]
3rank threshold=0.100 pairs=[RankPair(down_row=1, up_column=1), RankPair(down_row=3, up_column=3)]The simulator's quantile is a transparent teaching implementation. The repository uses NumPy's quantile behavior, so a production reproduction should compare exact selected indices rather than copying this helper into a trainer.
Try changing the rank ratio to 0.75. The retained pair list shrinks, but every pair still has matching down and up coordinates. Try changing one backbone score to a tie at the threshold. The strict comparison then makes the survivor count depend on the quantile value.
The paper validates Light-PEFT on GLUE, SuperGLUE, and question-answering tasks. Its foundation models are RoBERTa-Large, OPT-1.3B, and OPT-6.7B. Its PEFT structures are LoRA and Adapter. Those are the tested combinations, not a universal compatibility matrix.[1]
The GLUE table compares LoRA on RoBERTa-Large with Light-PEFT variants. The values below are paper-reported averages and speed measurements, not claims about current hardware.
| Method | Trainable params | Foundation retained | GLUE average | Training speed |
|---|---|---|---|---|
| Baseline LoRA | 0.8M | 100% | 93.3 | 1× |
| Light LoRA, lower pruning | 0.3M | 72% | 92.2 | 1.4× |
| Light LoRA, higher pruning | 0.3M | 67% | 91.9 | 1.6× |
The table's speed includes estimation before pruning. It was measured on a single NVIDIA TITAN RTX 24GB GPU with batch size 32 and sequence length 128. "1.6×" therefore means total wall-clock behavior under that protocol, not a guaranteed kernel multiplier on another GPU.[1]
Notice the trade: the 67% retained configuration uses the same rounded 0.3M trainable parameter count as the 72% configuration, but gives up 0.3 GLUE average points for the reported 0.2× additional speedup. The useful comparison is a Pareto choice under a declared task and budget, not "more pruning is always better."
Figure 4 measures RoBERTa-Large on an NVIDIA RTX 3090 with batch size 32 and sequence length 128. The Light-PEFT setting retains 67% of foundation parameters and 0.3M trainable parameters. The paper reports 32% lower model-weight memory, 40% lower activation memory, and 39% lower peak memory. Its ten-batch forward and backward timing is 2.2× faster than baseline LoRA.[1]
The peak-memory result follows the mechanism taught earlier. Removing dimensions reduces both weight storage and intermediate activation shapes. The 2.2× number is not a claim that every batch, sequence length, or GPU sees that ratio. It includes the measured workload and implementation.
Table 5 uses OPT-6.7B with batch size 96 and maximum length 100. It reports load memory and inference speed for foundation pruning and optional module pruning.
| Configuration | Foundation retained | Remaining LoRA modules | Inference speed | Load memory |
|---|---|---|---|---|
| Vanilla LoRA | 100% | 192 | 1× | 12.5G |
| Light-PEFT | 76% | 192 | 1.1× | 9.5G |
| Light-PEFT | 52% | 192 | 1.2× | 6.5G |
| Light-PEFT | 52% | 96 | 1.4× | 6.5G |
| Light-PEFT | 52% | 48 | 1.6× | 6.4G |
The paper therefore reports up to 1.6× inference speed and 48% lower load memory in this OPT-6.7B protocol. "48%" compares 6.5G with 12.5G approximately, and the 1.6× row also removes 75% of PEFT modules. Keep those axes together when reproducing the result.[1]
The evidence doesn't establish support or benefit for:
GLM-5.2 and DeepSeek-V4 are research directions for asking whether task-specific structured pruning transfers to newer architectures. Their names are not support evidence for this pinned release. A capability claim needs a model-specific implementation, loader test, benchmark protocol, and license check.
Ordinary multi-adapter serving relies on one shared base shape. Request A can select adapter A, request B can select adapter B, and both use the same frozen matrix dimensions. A structured Light-PEFT run physically removes heads and FFN coordinates from that base shape.
That creates a serving choice:
| Serving design | Benefit | New requirement |
|---|---|---|
| One pruned base per task | Smallest task-specific compute | Duplicate base weights and route requests to shape-specific workers |
| One unpruned base plus masks | Shared storage and shape compatibility | Masks may skip work only if kernels understand them; memory cost returns |
| Union of task survivors | One shared shape for a set of adapters | Union can approach the original width and erase savings |
| Runtime adapter rewrite | Flexible fleet | Physical tensor surgery, synchronization, and cache invalidation |
The paper's "plug-and-play" claim refers to switching masks and PEFT modules in its tested setup. It doesn't automatically mean that one production base can serve arbitrary adapters with one static kernel plan.[1]
KV caches and compiled kernels add more state. A server that caches a plan for 16 heads can't reuse it for a 12-head pruned base. A cache keyed only by model name and adapter ID can return incompatible tensors if the base shape or pruning seed changes. Include base revision, shape signature, and adapter revision in cache keys.
Early scores come from a prefix of one data distribution. In supervised fine-tuning, later batches can already differ from the prefix. In online RL, the policy changes the data it receives, so the shift can be stronger:
D_0.D_0.D_1, which can contain states absent from D_0.D_1 even if it looked quiet in D_0.This is a research concern, not a measured failure in the paper's static GLUE or QA protocol. An RL evaluation should log policy version, mask version, trajectory distribution, reward slices, and divergence before and after pruning. A held-out online stream can test whether reward collapse follows the physical cut.
If a system needs reversible exploration, keep the unpruned base or a set of checkpoints. A one-way physical slice is cheap to run but expensive to undo without rebuilding the original model.
The code is concise because it assumes a controlled research machine. A production adaptation needs extra checks.
Before loading adapter state, verify:
The source slices tensors in place and restores state dict entries by name. A different Transformers implementation can rename a module or store transposed weights. A name match isn't enough; compare tensor shapes.
Mask penalties and first-order scores depend on gradients. Record:
The repository uses separate attention and FFN mask learning-rate coefficients in MaskTrainer, then defaults both to one in arguments.py. A port that silently changes those multipliers changes the search problem.[2]
Time the complete workflow:
1clean process start
2 + model and tokenizer load
3 + dataset preparation
4 + estimation prefix
5 + mask and score serialization
6 + physical pruning and model rebuild
7 + continued fine-tuning
8 + evaluation and exportThe paper's Table 1 explicitly includes estimation before pruning. A benchmark that starts its stopwatch after the masks exist will overstate speedup. A benchmark that includes data download but not model load will answer a different question. State both wall-clock definitions.
Run these checks separately:
This order separates environment errors from score errors and shape errors. It also avoids burning a long GPU job before discovering that the saved peft_dir points to another dataset.
Light-PEFT combines pruning with PEFT; it isn't a replacement name for every efficient fine-tuning approach.
| Approach | Changes base shape? | Changes adapter shape? | Search timing | Main evidence |
|---|---|---|---|---|
| LoRA | No | Fixed rank | None | adapter task loss |
| QLoRA | No physical shape change | Fixed rank | None | quantized storage and adapter loss |
| AdaLoRA | Usually no permanent base shape | Dynamic rank allocation | During training | rank importance over time |
| LayerDrop | Can skip layers dynamically | Optional adapter | During training | layer execution policy |
| Light-PEFT | Yes, structured heads and FFN | Yes, modules and ranks | Early prefix, then slice | masks, output ratios, Taylor scores |
Light-PEFT's efficiency comes from paying for a change in the model's dense shapes. That makes its steady-state run different from a method that only gates execution or stores lower-precision weights.
When moving beyond RoBERTa NLU, ask these in order:
The code knows RoBERTa attention and layer classes. A decoder-only model may store fused QKV, grouped-query heads, rotary embeddings, parallel residual paths, or gated FFN matrices. A head slice must respect those layouts. An MoE layer adds expert routing and capacity rules that this code doesn't model.
Global FFN and rank quantiles assume scores share a meaningful scale. Layer normalization, module placement, and gradient scale can make one layer's numbers incomparable. Normalize or keep thresholds local only after checking the score distribution.
A training fork can slice PyTorch linear layers. A fused inference engine may require a separate kernel for each head count, tensor-parallel shard, or expert layout. Prove that the loader, compiler, quantizer, and serving cache all understand the new shape.
Static labels and online rewards answer different questions. For multi-task or RL use, hold out shifts and re-evaluate after every pruning boundary. Early importance isn't a lifetime contract.
Keep original base weights, original adapter state, selected indices, and a manifest. A mask vector alone can't reconstruct removed matrix rows unless the original checkpoint remains available.
weight × gradient scores.estimate.py writes .npy mask histories and .pkl PEFT records; train.py reads them before slicing tensors.peft/LICENSE is Apache-2.0.Course complete: Return to the curriculum roadmap. You have now read seventeen project repositories. Keep source maps, test important claims against pinned code and primary sources, and use measured receipts to separate promising research from proven production behavior. Use your run manifest as final proof-of-skill checklist.
Answer every question, then check your score. Score 75% or higher to mark this lesson complete.
8 questions remaining.
Light-PEFT: Lightening Parameter-Efficient Fine-Tuning via Early Pruning
Gu, N., Fu, P., Liu, X., Shen, B., Lin, Z., & Wang, W. · 2024 · Findings of ACL 2024
Light-PEFT Source Repository at 7b10c22
gccnlp · 2024
Light-PEFT Contributors
Light-PEFT contributors · 2026
Apache License in Light-PEFT peft Subdirectory
Light-PEFT contributors · 2024
Questions and insights from fellow learners.