This repository contains a complete, end-to-end reproducible workflow for:
- Setting up llama.cpp from scratch
- Converting and quantizing SmolLM2-1.7B base model
- Training custom LoRA adapters with PEFT
- Deploying LoRA-enhanced models for inference
Inspired by: The comprehensive llama.cpp guide by steelph0enix
- Build Instructions: Compile llama.cpp with GPU support
- Model Conversion: Convert HuggingFace models to GGUF format
- Quantization: Create optimized Q4_K_M quantized models
- Training Script: Train custom LoRA adapters using PEFT + Transformers
- GGUF Conversion: Convert trained adapters to llama.cpp-compatible format
- Validation Tools: Verify your LoRA adapters are correctly formatted
- Test Scripts: Compare base model vs LoRA-enhanced outputs
- CLI Usage: Run inference with llama-cli
- Server Mode: Deploy as OpenAI-compatible API
- Multiple Adapters: Load and combine multiple LoRAs
This experiment successfully:
- โ Trained a LoRA adapter on 100 Wikitext samples (1 epoch)
- โ Reduced training loss: 3.432 โ 3.094
- โ Created a 6.01 MB GGUF adapter file (192 tensors)
- โ Validated inference with llama.cpp
- โ Achieved 13-14 tokens/second generation speed
Trainable Parameters: 3.1M (0.18% of total model parameters)
This guide walks you through the entire process, from setting up llama.cpp to testing your trained LoRA adapter.
Following the llama.cpp guide:
Option A: Pre-built binaries (Windows - easiest)
winget install ggml.llamacppOption B: Build from source
# Clone repository
git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp
# Build with GPU support (CUDA/Vulkan/Metal)
cmake -B build
cmake --build build --config Release
# Executables will be in build/bin/Download from HuggingFace:
# Option 1: Using llama-cli (if installed via winget)
llama-cli -hf HuggingFaceTB/SmolLM2-1.7B-Instruct
# Option 2: Manual download
git lfs install
git clone https://huggingface.co/HuggingFaceTB/SmolLM2-1.7B-Instructcd llama.cpp
# Convert HuggingFace model to GGUF
python convert_hf_to_gguf.py \
../repos/SmolLM2-1.7B-Instruct \
--outfile ../SmolLM2.gguf \
--outtype f16# Quantize to Q4_K_M (4-bit, good quality/size balance)
./build/bin/llama-quantize \
../SmolLM2.gguf \
../SmolLM2.Q4_K_M.gguf \
Q4_K_MResult: SmolLM2.Q4_K_M.gguf (~1GB) - your base model!
llama-cli -m SmolLM2.Q4_K_M.gguf -p "Once upon a time" -n 50# Python 3.10+
python --version
# Install dependencies
pip install -r requirements.txtpython train_lora.pyThis will:
- Download SmolLM2-1.7B-Instruct from HuggingFace
- Load 100 Wikitext samples for training
- Train for 1 epoch (~2 hours on CPU)
- Save adapter to
./lora-output/
Customize Training: Edit these variables in train_lora.py:
MODEL_NAME = "HuggingFaceTB/SmolLM2-1.7B-Instruct"
LORA_RANK = 8 # Higher = more capacity
LORA_ALPHA = 16 # Scaling factor
NUM_EPOCHS = 1 # Training epochs
BATCH_SIZE = 1 # Adjust for your RAMcd ../../llama.cpp
python convert_lora_to_gguf.py \
--base ../repos/SmolLM2-1.7B-Instruct \
--outtype f16 \
../experiments/lora-feasibility/lora-output/Output: lora-output/Lora-Output-F16-LoRA.gguf
python validate_lora.pyChecks:
- โ Valid GGUF format
- โ Correct tensor count
- โ File integrity
# Using llama-cli (from lora-feasibility directory)
llama-cli \
-m ../../SmolLM2.Q4_K_M.gguf \
--lora lora-output/Lora-Output-F16-LoRA.gguf \
-p "Once upon a time" \
-n 100Lllamacpp/ # Root directory
โโโ llama.cpp/ # llama.cpp repository
โ โโโ build/bin/ # Compiled executables
โ โ โโโ llama-cli.exe # CLI inference tool
โ โ โโโ llama-server.exe # API server
โ โ โโโ llama-quantize.exe # Quantization tool
โ โ โโโ llama-export-lora.exe # LoRA merge tool
โ โโโ convert_hf_to_gguf.py # Model converter
โ โโโ convert_lora_to_gguf.py # LoRA converter
โ
โโโ repos/ # HuggingFace models
โ โโโ SmolLM2-1.7B-Instruct/ # Base model (HF format)
โ
โโโ SmolLM2.gguf # Converted base model (F16)
โโโ SmolLM2.Q4_K_M.gguf # Quantized model (~1GB)
โ
โโโ experiments/
โโโ lora-feasibility/ # This project!
โโโ train_lora.py # Main training script
โโโ validate_lora.py # GGUF validation
โโโ quick_test.py # Python-based testing
โโโ requirements.txt # Python dependencies
โโโ lora-output/ # Training outputs
โ โโโ adapter_model.safetensors
โ โโโ adapter_config.json
โ โโโ Lora-Output-F16-LoRA.gguf # Final adapter
โโโ README.md # This file
โโโ RESULTS.md # Detailed results
โโโ HOW_TO_RUN.md # Troubleshooting
| Parameter | Default | Description |
|---|---|---|
LORA_RANK |
8 | Rank of update matrices (4-64) |
LORA_ALPHA |
16 | Scaling factor (typically 2รrank) |
LORA_DROPOUT |
0.1 | Dropout rate for regularization |
MAX_SEQ_LENGTH |
256 | Maximum sequence length |
BATCH_SIZE |
1 | Training batch size |
NUM_EPOCHS |
1 | Number of training epochs |
LEARNING_RATE |
2e-4 | AdamW learning rate |
By default, we target attention layers:
target_modules=["q_proj", "v_proj", "k_proj", "o_proj"]You can also target FFN layers:
target_modules=["q_proj", "v_proj", "k_proj", "o_proj", "gate_proj", "up_proj", "down_proj"]Replace the dataset loading in train_lora.py:
# Option 1: From HuggingFace
dataset = load_dataset("your-username/your-dataset", split="train")
# Option 2: From local file
from datasets import Dataset
with open("your_data.txt") as f:
texts = [line.strip() for line in f]
dataset = Dataset.from_dict({"text": texts})
# Option 3: JSON/CSV
dataset = load_dataset("json", data_files="your_data.json")Load multiple adapters simultaneously:
llama-cli -m base.gguf \
--lora adapter1.gguf,adapter2.gguf \
-p "Your prompt"Apply different scaling factors:
llama-cli -m base.gguf \
--lora-scaled adapter1.gguf:0.8,adapter2.gguf:1.2 \
-p "Your prompt"Create a single merged model file:
llama-export-lora \
-m base.gguf \
--lora adapter.gguf \
-o merged-model.ggufRun as an OpenAI-compatible API server:
llama-server \
-m base.gguf \
--lora adapter.gguf \
--port 8080Test with curl:
curl http://localhost:8080/v1/completions \
-H "Content-Type: application/json" \
-d '{
"prompt": "Once upon a time",
"max_tokens": 50
}'# Reduce batch size
BATCH_SIZE = 1
# Increase gradient accumulation
gradient_accumulation_steps = 8
# Reduce LoRA rank
LORA_RANK = 4๐๏ธ Architecture Overview
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Complete Workflow โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
1. Base Model Setup (llama.cpp)
HuggingFace Model โ convert_hf_to_gguf.py โ GGUF (F16)
โ
GGUF (F16) โ llama-quantize โ GGUF (Q4_K_M) โ Base Model Ready
2. LoRA Training (This Project)
Dataset โ train_lora.py โ adapter_model.safetensors
โ
adapter_model.safetensors โ convert_lora_to_gguf.py โ LoRA.gguf
3. Inference (llama.cpp)
Base Model (GGUF) + LoRA (GGUF) โ llama-cli/server โ Output
- llama.cpp - High-performance inference engine by Georgi Gerganov
- The comprehensive llama.cpp guide by steelph0enix - Excellent tutorial that inspired this project
- PEFT - Parameter-Efficient Fine-Tuning library by HuggingFace
- SmolLM2 - Compact language model by HuggingFace
- Reduce dataset size for faster iteration
If llama.cpp executables fail:
- Download pre-built binaries from llama.cpp releases
- Or install via package manager:
winget install ggml.llamacpp - Or use Python bindings:
pip install llama-cpp-python
See HOW_TO_RUN.md for detailed solutions.
- llama.cpp - Inference engine
- PEFT - Parameter-Efficient Fine-Tuning
- SmolLM2 - Base model
- The comprehensive llama.cpp guide - Inspiration
If you use this work, consider citing:
@misc{smollm2-lora-experiments,
title={LoRA Fine-tuning Experiments for SmolLM2},
author={Your Name},
year={2026},
url={https://github.com/yourusername/lora-experiments}
}MIT License - See LICENSE file for details
- Inspired by steelph0enix's llama.cpp guide
- Built with llama.cpp, HuggingFace PEFT, and Transformers
- Trained on the SmolLM2-1.7B-Instruct model by HuggingFace
Happy Fine-tuning! ๐
For questions or issues, please open a GitHub issue.