🧮 Zephyr-FVM: Fine-tuning Zephyr-7B for Finite Volume Numerical Methods
Fine-tuning Zephyr-7B for Finite Volumes: Methodology and Results
Context and Goal
I wanted to test fine-tuning on a domain I know well: finite volume methods in numerical simulation. The goal was to make Zephyr-7B able to explain the concepts and schemes clearly, so it could answer technical questions on the topic.
Data Preparation: The Heart of the Project
The quality of a fine-tune depends on the training data. I built a dataset of a few question/answer pairs covering:
- Theoretical concepts:
- “What is the CFL condition and how do you apply it to an explicit scheme?”
- “What is the difference between an upwind scheme and a centered scheme for discretizing a convective term?”
- Practical cases:
- “How do you handle a Neumann boundary condition in a finite volume solver?”
- “What are the stability criteria for an implicit scheme applied to the heat equation?”
- Method comparisons:
- “What are the advantages and disadvantages of explicit vs implicit schemes?”
- “When should an upwind scheme be preferred over a centered scheme?”
Data format:
Each example follows a standard format for SFTTrainer:
{
"text": "### Instruction: [Question about finite volumes]\n### Response: [Detailed technical answer]"
}
Example:
{
"text": "### Instruction: Explain the CFL condition for an explicit finite volume scheme.
### Response: The CFL (Courant-Friedrichs-Lewy) condition is a stability criterion for explicit numerical schemes. For a convection problem (∂u/∂t + c∂u/∂x = 0), it is written as Δt ≤ Δx/|c|, where Δt is the time step, Δx the spatial step, and c the convection speed. For diffusion (∂u/∂t = ν∂²u/∂x²), it becomes Δt ≤ Δx²/(2ν). These conditions ensure that numerical information does not propagate faster than the physics of the problem."
}
Why this format?
- Clear structure: The model learns to distinguish the question (
Instruction) from the answer (Response). - Compatibility:
trl’sSFTTraineris optimized for this format, which simplifies training.
Fine-tuning Methodology: Zephyr-7B + LoRA in 4-bit
Model and Technique Choice
I selected Zephyr-7B for its instruction-following ability and its size, which is suitable for a consumer GPU. To reduce memory usage, I used:
- LoRA (Low-Rank Adaptation): A technique that trains only a few additional weights (the “adapters”), without modifying the base model.
- 4-bit quantization: Reduces weight precision to save memory (via
bitsandbytes).
Technical Setup
- Model loading:
from transformers import AutoModelForCausalLM, BitsAndBytesConfig bnb_config = BitsAndBytesConfig( load_in_4bit=True, # 4-bit quantization bnb_4bit_quant_type="nf4", # Optimal quantization type bnb_4bit_compute_dtype=torch.float16, # FP16 computation ) model = AutoModelForCausalLM.from_pretrained( "HuggingFaceH4/zephyr-7b-beta", quantization_config=bnb_config, device_map="auto", # Automatically uses the GPU ) - LoRA setup:
from peft import LoraConfig lora_config = LoraConfig( r=8, # LoRA matrix rank (smaller = fewer parameters) lora_alpha=32, # Scaling factor target_modules=["q_proj", "k_proj", "v_proj", "o_proj"], # Target layers lora_dropout=0.05, # Dropout to reduce overfitting bias="none", # No bias in the adapters task_type="CAUSAL_LM", # Task type (language model) ) - Preparing the model for LoRA:
model = prepare_model_for_kbit_training(model) # Prepares the model for LoRA model = get_peft_model(model, lora_config) # Applies LoRA
Memory Optimizations
To avoid CUDA out of memory errors on a T4 GPU (15 GB of VRAM), I:
- Reduced the batch size:
per_device_train_batch_size=2. - Used gradient accumulation:
gradient_accumulation_steps=8(equivalent to a batch size of 16). - Limited the sequence length:
max_seq_length=256. - Cleared memory before training:
import torch, gc torch.cuda.empty_cache() gc.collect()
Training: Parameters and Results
Training Parameters
from transformers import TrainingArguments
training_args = TrainingArguments(
output_dir="zephyr-volumes-finis", # Output directory
per_device_train_batch_size=2, # Batch size
gradient_accumulation_steps=8, # Gradient accumulation
learning_rate=2e-4, # Learning rate
num_train_epochs=3, # Number of epochs
fp16=True, # Mixed precision (FP16)
save_steps=50, # Periodic checkpoints
logging_steps=10, # Periodic logs
optim="paged_adamw_8bit", # Memory-efficient optimizer
report_to="none", # Disable Weights & Biases
)
Running Training
from trl import SFTTrainer
trainer = SFTTrainer(
model=model,
args=training_args,
train_dataset=dataset, # Prepared dataset
peft_config=lora_config, # LoRA configuration
max_seq_length=256, # Maximum sequence length
)
trainer.train() # Start training
Results and Observations
- Duration: ~ minutes for 3 epochs on a few examples (T4 GPU).
- Loss progression:
- Epoch 1: Loss ~0.5 (the model is learning the answer structure).
- Epoch 2: Loss ~0.25 (the answers become more precise).
- Epoch 3: Loss ~0.15 (stabilization, correct generalization).
- Answer quality:
- The model generates precise technical explanations, even for questions not present in the dataset.
- The responses are structured and context-aware (for example, convection/diffusion distinctions).
Example generated answer for “What are the advantages of an implicit scheme?”:
*“An implicit scheme has three major advantages:
- Unconditional stability: Unlike explicit schemes, it is not subject to the CFL condition, which allows larger time steps (Δt ≫ Δx²/(2ν) for diffusion).
- Time accuracy: By avoiding restrictions on Δt, it better captures slow dynamics (for example, diffusion over long periods).
- Robustness: Less sensitive to numerical instabilities, even for stiff problems (for example, equations with strong source terms). Drawback: It requires solving a linear system at each time step, which increases the computational cost (tridiagonal matrix for 1D problems).”*
Validation and Limits
To validate the model, I tested it on questions not present in the dataset:
- “How do you discretize a source term in the convection-diffusion equation?” → A coherent answer explaining how the source term is integrated into the flux balance.
- “What is the influence of the mesh on the accuracy of a finite volume scheme?” → A detailed answer about truncation error and convergence.
Identified limits:
- Limited precision for very specific problems (for example, 3D unstructured meshes).
- Dataset dependence: The model generalizes well within its training domain, but may lack detail for uncovered cases.
Future Improvements
- Expand the dataset:
- Add examples on non-uniform meshes, 2D/3D problems, and high-order schemes.
- Include cases with source terms or chemical reactions (for example, a reaction-diffusion equation).
- Optimize training:
- Test with more epochs (5-10) to improve accuracy.
- Use a validation set to evaluate generalization.
- Practical integration:
- Develop an API to interact with the model through a web interface.
- Create a Jupyter notebook with interactive PDE-solving examples.
Conclusion
This project shows that a large model like Zephyr-7B can be adapted to a specific technical domain with little data and training time. The model already answers finite volume questions correctly and explains the concepts clearly.
Why is this useful?
- Saves time when looking for explanations.
- Gives pedagogical answers adapted to the audience.
- Can be extended to other numerical methods.
Next steps:
- …
Project link: GitHub Gist