Dynamic BB-OPLoRA is a subspace-aware low-rank adaptation method with a rigid top-singular core and a flexible border block.
Dynamic BB-OPLoRA: Rigid Core, Flexible Border for Subspace-Aware Low-Rank Adaptation
Ali Sharifian, Julian Herold, Tara Gheshlaghi, and Alexander Schug
Accepted at the ICML'26 Workshop on CoLoRAI — The 2nd Workshop on Connecting Low-rank Representations in AI (2026).
Publication date: 29 May 2026.
If this repository or method contributes to your work, please cite the accepted paper:
@inproceedings{sharifian2026dynamic,
title = {Dynamic {BB-OPLoRA}: Rigid Core, Flexible Border for Subspace-Aware Low-Rank Adaptation},
author = {Sharifian, Ali and Herold, Julian and Gheshlaghi, Tara and Schug, Alexander},
booktitle = {ICML'26 Workshop on CoLoRAI -- The 2nd Workshop on Connecting Low-rank Representations in AI},
year = {2026},
month = may,
url = {https://openreview.net/forum?id=eog19l00te}
}The same entry is available in CITATION.bib. GitHub's Cite this repository menu reads the structured metadata in CITATION.cff.
- The pretrained linear layer is kept frozen throughout adaptation.
- Only the LoRA factors (A) and (B) are trainable.
- A fixed SVD basis of the frozen weight defines the top-(K) singular subspace.
- The top-(K) subspace is partitioned into a rigid core of size (K-N) and a flexible border of size (N).
- The outside update is projected away from the top-(K) left and right singular subspaces.
- The border update is induced implicitly from the same (BA) product and constrained by a stiffness-weighted budget.
- During a predefined training window, a pressure-driven controller adjusts the border budget according to the gradient signal.
The default experiment trains Qwen2.5-7B on Commonsense-170k with:
BB_K=20BB_B=0.2LORA_R=32- target modules
q_proj,v_proj,up_proj,down_proj,o_proj
The figure below summarizes and compares LoRA, OPLoRA, and Dynamic BB-OPLoRA.
scripts/
bb_oplora_nc_optA_stiff.py # Dynamic BB-OPLoRA layer and model patching
train_bb_oplora_nc_commonsense170k_optA_stiff.py
eval_oplora_lm_eval.py # merge adapter into base weights, then run lm-eval
sanitycheck_bb_oplora_nc_optA_stiff_v2.py
make_paper_table3_oplora.py
lm_eval_tasks/ # local lm-eval YAMLs with pinned dataset revisions
slurm/ # cluster launch scripts
docs/ # reproducibility notes and dataset revision audit
CITATION.bib # ready-to-copy paper citation
CITATION.cff # GitHub citation metadata
Runtime outputs are written under logs/, results/, and the configured cache directories.
Python 3.10 or newer is recommended. Install dependencies in a fresh environment:
python -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -r requirements.txtFor CUDA-enabled PyTorch, install the wheel matching the local CUDA/driver stack if the generic requirement does not match the machine.
Optional cache locations are set in llm_env.sh. By default, caches are placed under the repository directory.
Run from the repository root:
source llm_env.sh
python -u scripts/train_bb_oplora_nc_commonsense170k_optA_stiff.pyOn a Slurm cluster, submit from the repository root so relative log paths resolve correctly:
sbatch slurm/04_train_bb_oplora_nc_routeA_paper_optA_stiff.sbatchIf the cluster uses environment modules, set HPC_MODULE before submission. To use a specific virtual environment, set:
export VENV_ACTIVATE=/path/to/venv/bin/activateThe Slurm headers are cluster-neutral. Fill these optional lines only if the target cluster requires them:
##SBATCH --partition=
##SBATCH --account=
##SBATCH --qos=
##SBATCH --constraint=The active resource lines can also be edited for the target machine:
#SBATCH --nodes=1
#SBATCH --ntasks=1
#SBATCH --cpus-per-task=16
#SBATCH --gres=gpu:1
#SBATCH --time=01:00:00
#SBATCH --output=logs/...
#SBATCH --error=logs/...For clusters that require a module, set it before submission:
export HPC_MODULE=<module-name>Leave HPC_MODULE empty when using a normal virtual environment or conda environment.
The .sbatch files are configurable launch scripts. Defaults reproduce the run described above unless a value is changed in the script or exported before submission.
Cluster/runtime parameters:
| Name | Where | Meaning |
|---|---|---|
HPC_MODULE |
train/eval/cache | Optional environment-module name to load. Empty by default. |
VENV_ACTIVATE |
train/eval/cache | Optional path to a virtualenv activation script. |
OMP_NUM_THREADS |
train/eval/cache | CPU thread count; defaults to SLURM_CPUS_PER_TASK inside Slurm. |
MKL_NUM_THREADS |
train/eval/cache | MKL thread count; defaults to SLURM_CPUS_PER_TASK inside Slurm. |
WS |
llm_env.sh |
Optional cache root; defaults to the repository root. |
Training data/model parameters:
| Name | Default | Meaning |
|---|---|---|
SEED |
999 |
Training random seed. |
MODEL_ID |
Qwen/Qwen2.5-7B |
Base Hugging Face model. |
DATASET_ID |
zwhe99/commonsense_170k |
Training dataset. |
TARGET_MODULES |
q_proj,v_proj,up_proj,down_proj,o_proj |
Linear-module suffixes wrapped by Dynamic BB-OPLoRA. |
OUTDIR |
results |
Training output directory. |
LEARNING_RATE |
2e-4 |
Optimizer learning rate. |
NUM_TRAIN_EPOCHS |
1 |
Number of epochs. |
WEIGHT_DECAY |
0.0 |
Optimizer weight decay. |
LR_SCHEDULER |
cosine |
Trainer LR scheduler. |
WARMUP_RATIO |
0.03 |
Trainer warmup fraction. |
LoRA and Dynamic BB-OPLoRA parameters:
| Name | Default | Meaning |
|---|---|---|
LORA_R |
32 |
LoRA rank. |
LORA_ALPHA |
32 |
LoRA scaling alpha. |
LORA_DROPOUT |
0.05 |
LoRA dropout. |
BB_K |
20 |
Size of the protected top singular subspace. |
BB_B |
0.2 |
Border fraction/size setting used by the wrapper. |
BB_ETA |
0.00 |
Initial border budget. |
BB_EPS_CAP |
1e-8 |
Numerical epsilon for cap enforcement. |
BB_BORDER_STIFF_LAMBDA |
1.0 |
Border stiffness strength. |
BB_BORDER_STIFF_GAMMA |
2.0 |
Border stiffness shape. |
BB_STIFF_LAMBDA |
same as BB_BORDER_STIFF_LAMBDA |
Backward-compatible alias. |
Pressure-controller parameters:
| Name | Default | Meaning |
|---|---|---|
BB_ENABLE_PRESSURE |
1 |
Enable pressure-driven budget updates. |
BB_BETA_EMA |
0.95 |
EMA coefficient for pressure. |
BB_TAU_UP |
0.00222 |
Pressure threshold for increasing eta. |
BB_TAU_DOWN |
0.0 |
Pressure threshold for decreasing eta. |
BB_DELTA_ETA |
0.0125 |
Additive eta update size. |
BB_ETA_MAX |
0.8 |
Maximum border budget. |
BB_EPS_PRS |
1e-8 |
Numerical epsilon for pressure. |
BB_PRESSURE_LOG_EVERY |
12 |
Pressure logging interval. |
BB_WINDOW_START_PCT |
0.11 |
Start of pressure-update window as fraction of total steps. |
BB_WINDOW_END_PCT |
0.74 |
End of pressure-update window as fraction of total steps. |
Evaluation/cache parameters:
| Name | Default | Meaning |
|---|---|---|
BASE |
Qwen/Qwen2.5-7B |
Base model used for merge/evaluation. |
BB_OPLORA_DIR |
$OUTDIR/qwen2p5_7b_bb_oplora_nc_commonsense170k |
Trained checkpoint directory. |
EVAL_DIR |
$OUTDIR/eval_table3_bb_oplora_nc_allchecks_full |
Evaluation output directory. |
SHARED_EVAL_CACHE_ROOT |
$RUNROOT/hf_cache_eval |
Shared cache root. |
BOOLQ_REV |
pinned SHA | Optional BoolQ dataset revision override. |
HELLASWAG_REV |
pinned SHA | Optional HellaSwag dataset revision override. |
MATHQA_REV |
pinned SHA | Optional MathQA dataset revision override. |
WINOGRANDE_REV |
pinned SHA | Optional WinoGrande dataset revision override. |
OPENBOOKQA_REV |
pinned SHA | Optional OpenBookQA dataset revision override. |
SOCIAL_IQA_REV |
pinned SHA | Optional SocialIQA dataset revision override. |
After training has written results/qwen2p5_7b_bb_oplora_nc_commonsense170k, run:
python scripts/eval_oplora_lm_eval.py \
--base_model Qwen/Qwen2.5-7B \
--bb_oplora_dir results/qwen2p5_7b_bb_oplora_nc_commonsense170k \
--out_json results/eval_table3_bb_oplora_nc_allchecks_full/lm_eval_results_full.json \
--device cuda:0 \
--batch_size autoOr use:
sbatch slurm/OPLoRA_eval_all_checks_full.sbatch