Case study · Generative AI
Fine-Tuning LLaMA-2 7B for Python Code Generation
Executive summary
Can a general model become a useful local coding model?
Improve Python generation without the memory cost of full-parameter fine-tuning or dependence on a cloud API.
Distill 60,000 raw samples to 13,914 functional examples, then fine-tune 4-bit LLaMA-2 7B Chat with QLoRA.
Pass@1 rose 10.4% and Pass@5 rose 19.4% relative to the untuned chat model; local inference reached 15.67 tokens/s.
The result improves the chosen base model but remains well behind CodeLlama, showing the limit of task adaptation without code-specific pretraining.
1. Problem & Constraint
Large Language Models have transformed software development, but their scaling creates a deployment barrier: high-performing models require substantial GPU memory and are commonly accessed through cloud APIs. For students, researchers, and small teams, cloud-only access introduces cost, latency, privacy concerns, and external dependence.
Smaller open models like LLaMA-2 7B can run on consumer GPUs, but their code-generation quality is weaker than that of larger or code-specialized models. This motivates methods that adapt smaller models to specialized tasks without full parameter fine-tuning.
Parameter-Efficient Fine-Tuning (PEFT) provides a practical solution. LoRA injects low-rank trainable matrices into transformer layers, reducing trainable parameter count. QLoRA extends this by quantizing the base model to 4-bit precision and training LoRA adapters on top, enabling 7B-scale adaptation on modest hardware.
The project objective was deliberately practical: improve executable Python generation enough to measure a gain over the untuned chat model, then package the result for responsive local inference on a consumer GPU.
2. Data Collection & Curation
Source Datasets
| Dataset | Raw Size | Description | License |
|---|---|---|---|
| FlyTech Python Code | ~42,000 pairs | Python scripts with comments, docstrings, structured tasks | MIT |
| Python Code Instructions 18k (Alpaca-style) | ~18,000 pairs | Instruction prompts and Python responses | CC BY-NC 4.0 |
Filtering Pipeline
The raw datasets contained general scripting tasks, web-development snippets, machine-learning examples, duplicates, empty descriptions, comments, docstrings, and non-functional fragments. We applied a multi-stage filtering pipeline:
Impact of Repetition Threshold
| Max Repetition Ratio (rho) | Final Dataset Size |
|---|---|
| 0.7 | 20,335 |
| 0.6 | 20,270 |
| 0.5 | 19,876 |
| 0.4 (selected) | 18,487 |
| 0.3 | 15,577 |
Dataset Distillation Summary
| Stage | Sample Count | Logic Density |
|---|---|---|
| Raw Combined | 60,000 | Low |
| Filtered (Logic Only) | 34,000 | Medium |
| Refined (Functional/Clean) | 21,542 | High |
| Final (Functional Only) | 13,914 | Very High |
Why This Matters
The curation pipeline was designed to concentrate the training signal on executable Python rather than comments, repeated boilerplate, or incomplete fragments. The project did not independently ablate every filtering stage, so its individual causal contribution should not be inferred from the final benchmark gain.
3. Methodology
Key Pivot: Base Model to Chat Model
An initial base-model strategy produced inconsistent output structure and weak task alignment, which made Pass@k evaluation noisy. Moving to LLaMA-2 Chat forced a clearer instruction template and stable response boundary. The pivot was less about choosing a fashionable model and more about making outputs easier to execute, compare, and score.
Prompt Formatting
All examples were converted into the LLaMA-2 native instruction format using the chat template with [INST] markers. The tokenizer maximum length was set to 512 tokens with right-side padding, and the padding token was mapped to the end-of-sequence token so the model could learn clear termination behavior during generation.
Completion-Only Instruction Masking
Standard supervised fine-tuning can compute loss over every token in the prompt-response sequence. Here, tokens belonging to the instruction and template prefix were assigned the label -100, which PyTorch cross-entropy ignores. This aligned the optimization objective with the response tokens after the [/INST] marker instead of spending loss on reproducing the prompt.
Hyperparameter Optimization
We performed hyperparameter optimization with Optuna using a reduced 5% subset of the training data. Each trial used an 80/20 train-validation split. We ran 45 trials with Tree-structured Parzen Estimator search and pruning for underperforming trials.
| Hyperparameter | Optimal Value |
|---|---|
| Learning Rate | 2.9089 x 10^-5 |
| LR Scheduler | Cosine |
| LoRA Rank (r) | 8 |
| LoRA Alpha | 32 |
| LoRA Dropout | 0.0961 |
| Batch Size | 4 |
| Warmup Ratio | 0.0693 |
QLoRA Fine-Tuning
The pretrained LLaMA-2 7B Chat weights were loaded in 4-bit NormalFloat format with double quantization. LoRA adapters were inserted into attention and feed-forward decoder modules while keeping the original base-model weights frozen. Only adapter parameters were trainable, representing less than 1% of total model parameters.
Training was performed on Kaggle dual-T4 GPUs using cross-entropy loss on response tokens only. Training was stopped during the second epoch because validation loss continued to decrease only marginally (below ~0.001), reducing overfitting risk.
Hardware & Infrastructure
Data preparation: Kaggle/Colab CPU or T4 GPU. HPO: Kaggle dual-T4 or P100. Final fine-tuning: Kaggle dual-T4 GPUs. Local inference: Ollama on RTX 3060/4060-class GPU.
4. Results
Quantization Impact
| Model Format | Memory Usage | Relative Change |
|---|---|---|
| FP16 | 28 GB | — |
| 4-bit NF4 | 5.2 GB | 81.4% reduction |
Quantization substantially reduced memory requirements. The 81.4% memory reduction enabled full training and deployment workflow without large data-center GPUs. We observed only a small quantitative performance difference between non-quantized and quantized inference.
HumanEval Pass@k Comparison
| Metric | Untuned LLaMA-2 7B Chat | Fine-Tuned Model | CodeLlama |
|---|---|---|---|
| Pass@1 | 0.1159 | 0.1280 | 0.2915 |
| Pass@3 | 0.1610 | 0.1927 | 0.4683 |
| Pass@5 | 0.1890 | 0.2256 | 0.5159 |
BLEU & CodeBLEU
| Metric | Score |
|---|---|
| BLEU | 10.2703 |
| CodeBLEU | 0.2363 |
BLEU remained limited because many programming problems permit multiple correct implementations with different surface forms. CodeBLEU is more informative because it considers code structure, syntax, and data-flow. Functional testing (HumanEval) remains the more important criterion for code generation.
Local Ollama Inference
| Metric | Value |
|---|---|
| Time to First Token | 0.52 seconds |
| Average Generation Speed | 15.67 tokens/s |
| Peak VRAM Usage | 5.1 GB |
| Hardware | NVIDIA RTX 4050 |
Evaluation Evidence
5. Limitations
Limited dataset scope. The final dataset focuses on Python and algorithmic tasks. It does not cover the full diversity of real software engineering work (web frameworks, data pipelines, concurrent programming, etc.).
Metrics cannot guarantee correctness. BLEU and CodeBLEU measure surface similarity, not functional correctness. A syntactically similar solution can still fail hidden test cases.
Narrow functional evaluation. HumanEval contains only 164 problems and may not represent broader production scenarios. More diverse benchmarks (MBPP, SWE-bench) would provide a fuller picture.
Fine-tuning cannot replace code-specific pretraining. The fine-tuned model remains significantly below CodeLlama on HumanEval, indicating that task-specific QLoRA improves LLaMA-2 but cannot fully compensate for limited code-specific pretraining.
Single-language deployment. Deployment was tested for Python code generation only and was not extended to multilingual code generation.
No component ablation. The final gain reflects the complete pipeline—data curation, prompt format, masking, hyperparameters, and QLoRA—not a controlled estimate of any one component's contribution.
The defensible conclusion is that parameter-efficient adaptation improved this base model within the measured task and hardware envelope—not that it produced a production-grade coding assistant.
6. Practical Recommendations
For teams considering similar fine-tuning projects on constrained hardware:
Invest heavily in data curation
Signal: The largest operational change was reducing 60,000 raw samples to 13,914 higher-density functional examples through teacher-model filtering and heuristic cleaning.
Action: Always clean before training. Remove comments, docstrings, duplicates, and repetitive boilerplate. Quality matters more than quantity.
Use completion-only instruction masking
Signal: Masking the prompt forces the loss function to focus on the code response rather than the instruction template.
Action: Apply -100 labels to all prompt tokens. This is a simple change with consistent benefits for instruction-tuned models.
Use QLoRA for hardware-constrained projects
Signal: 4-bit NF4 quantization reduced measured memory from 28 GB to 5.2 GB, while the complete fine-tuning pipeline improved HumanEval results over the untuned model.
Action: Use NormalFloat4 quantization with double quantization and paged optimizers. LoRA rank 8 with alpha 32 is a strong starting configuration.
Optimize hyperparameters on a data subset
Signal: Optuna with 45 trials on 5% of training data found effective configurations at low compute cost.
Action: Use Tree-structured Parzen Estimator search with early pruning. Focus search on learning rate, LoRA rank, dropout, and warmup ratio.
What NOT to do
Do not skip data cleaning in favor of larger datasets. Do not compute loss over prompt tokens when fine-tuning instruction-tuned models. Do not expect fine-tuning alone to match code-specialized pretrained models like CodeLlama.
7. Reproducibility
Environment: Python 3.10+, PyTorch, Hugging Face Transformers, PEFT, bitsandbytes, Optuna, Kaggle dual-T4 GPUs.
Base model: meta-llama/Llama-2-7b-chat-hf (4-bit NF4 quantized).
Evaluation: HumanEval (164 Python problems), BLEU, CodeBLEU.
Deployment: Ollama local inference on consumer NVIDIA GPU.
8. Future Work
- Repeat the experiment with a newer code-specialized base model to separate the value of adaptation from the limitations of LLaMA-2 pretraining.
- Expand the dataset with multilingual programming examples, edge cases, debugging tasks, and repository-level contexts.
- Evaluate preference optimization or feedback-based tuning after establishing stronger supervised baselines.
- Use curriculum-based fine-tuning to present simpler tasks before harder edge cases.
- Integrate with development environments for real-time code assistance and richer human-in-the-loop evaluation.
- Evaluate on additional benchmarks such as MBPP and SWE-bench for broader coverage.