Enterprise DNA
Guide Intermediate General

LoRA Fine TuningTutorial: GPU Free Setup Guide

A practical LoRA fine tuning tutorial that runs on free GPU tiers like Colab and Kaggle, with real code, real settings, and an honest look at the trade-offs.

Sam McKay |
LoRA Fine TuningTutorial: GPU Free Setup Guide

Updated 28 September 2026: Clarified that this guide uses free hosted GPUs rather than no GPU, added a practical notebook outline, data preparation and troubleshooting guidance, and retained the existing LoRA workflow and settings advice.

What LoRA actually is

LoRA stands for Low-Rank Adaptation. It is a parameter-efficient fine-tuning method introduced in 2021 by Hu and colleagues as a way to adapt large pretrained models without retraining every weight. The core idea is simple and worth understanding because everything else flows from it.

A standard transformer has weight matrices, mostly in the attention and feed-forward layers, with dimensions like 4096 by 4096. That is roughly 16 million parameters per matrix. Full fine-tuning updates all of them, which means storing gradients, optimizer states, and a copy of the model for every training step. On a 7 billion parameter model, that adds up to many tens of gigabytes of VRAM just to update, before you even count the model weights themselves.

LoRA freezes the original weights entirely. It then injects two small trainable matrices alongside each target weight. If the original weight W is d by k, LoRA adds a pair of matrices A and B, where A is d by r and B is r by k, and r is a small rank like 8 or 16. The product A times B is d by k, the same shape as W, so it can be added in. During training, only A and B get gradient updates. The number of trainable parameters drops by orders of magnitude, often to less than one percent of the full model.

At inference time you have two options. You can keep the LoRA adapters separate and load them on top of the base model when you need them, which is great for swapping skills in and out. Or you can merge the adapter weights back into the base model and save a single set of weights, with no runtime overhead at all.

QLoRA is the variant most people actually use on free GPUs. It combines LoRA with 4-bit quantization of the frozen base model, so the base weights take roughly a quarter of the memory they would otherwise need. Dettmers and coauthors introduced this in 2023, and it is what makes 7B and 13B models trainable on a 16GB consumer card or some free notebook GPU sessions.

The marketing version of LoRA talks about “training huge models on a laptop.” The honest version is that LoRA is a genuinely clever mathematical trick that lets you adapt a frozen model cheaply. The savings are real. They are not magic, and the quality ceiling is lower than full fine-tuning for tasks that need deep behavior change.

What “GPU free” means in this guide

GPU free does not mean LoRA training works without a GPU. It means you do not need to own or configure one yourself.

Google Colab, Kaggle Notebooks, and Lightning AI Studios can provide temporary hosted GPU access through a browser-based notebook. Availability, session limits, and GPU type vary by account, region, and current demand, so treat free access as a useful development environment rather than infrastructure you control.

If your first goal is to learn the process, a small model and a few hundred clean examples are enough. If your goal is a production-quality adapter, use the free environment to validate the data and settings first. Then move the proven run to paid compute where you can finish it without a disconnect.

Setup and authentication

The fastest path to a working LoRA setup on free infrastructure is Google Colab, Kaggle Notebooks, or Lightning AI Studios. All three give you a Jupyter environment with a free GPU and zero local setup.

For Colab, open a new notebook, then in the menu choose Runtime, then Change runtime type, then select a GPU accelerator if one is available. Free sessions can disconnect after idle periods or time limits, and the allocated hardware can change between sessions.

You will need a Hugging Face account to pull the base model and push your trained adapter. Create an account at huggingface.co if you do not have one. Then go to Settings, then Access Tokens, then create a new token with write scope. Copy the token into your notebook with the userdata helper in Colab.

Install the libraries. The current versions of the relevant packages are what you should grab, and pinning them avoids the usual breakage from upstream API drift.

In a Colab cell, run:

!pip install -q transformers peft trl datasets bitsandbytes accelerate

The bitsandbytes package is what provides the 4-bit quantization kernels that QLoRA depends on. The trl package wraps Hugging Face Trainer with supervised fine-tuning helpers that handle the chat template and padding for you.

For a private or gated model, accept the license on the model’s Hugging Face page first, then authenticate. In a notebook cell write from huggingface_hub import login, then login with your token. The CLI alternative is huggingface-cli login, which prompts you in a terminal.

If you want to push your finished adapter to the Hub, you will also need to call trainer.push_to_hub with your repository name. The token you stored earlier gives that write access.

Prepare your training data before opening the notebook

Your data matters more than the choice between rank 8 and rank 16.

For instruction tuning, JSON Lines is a practical format. Each line should be one complete training example with an instruction, optional context, and the answer you want the model to produce.

{"instruction":"Classify this support request","input":"I was charged twice for my subscription.","output":"billing_issue"}
{"instruction":"Classify this support request","input":"I cannot reset my password.","output":"account_access"}

Do not mix contradictory formats in the same dataset. If half your examples produce a label and the other half produce a paragraph, the model gets an unclear training signal. Use the same prompt structure, output format, tone, and level of detail that you expect at inference.

Keep a held-out evaluation set before training. A simple split of 80 percent training and 20 percent evaluation is fine for a small first project. Do not evaluate only on examples the adapter has already seen. That tells you whether it memorised the data, not whether it learned the task.

For a broader grounding in when fine-tuning is the right choice, see the Fine-Tuning LLM Tutorial: A Beginner’s Practical Guide.

First working example

Below is the structure of a complete fine-tuning run for a small compatible open model on a free hosted GPU. Start with a model that fits the memory you actually receive, rather than choosing the largest model you can find.

The dataset is a small instruction-following set in Alpaca format, which has an instruction, an optional input, and an expected output. You can swap in your own JSON Lines file in the same shape.

Load the model and tokenizer. We use AutoModelForCausalLM and BitsAndBytesConfig to load in 4-bit. The bnb_4bit_quant_type field set to nf4 is the recommended setting from the QLoRA paper. The compute dtype of bfloat16 works on some hardware, though float16 is a safer fallback if you see NaNs.

A basic loading cell looks like this. Replace your-compatible-open-model with a checkpoint whose licence permits your intended use.

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig

model_id = "your-compatible-open-model"

quant_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_use_double_quant=True,
    bnb_4bit_compute_dtype=torch.float16,
)

tokenizer = AutoTokenizer.from_pretrained(model_id)
tokenizer.pad_token = tokenizer.eos_token

model = AutoModelForCausalLM.from_pretrained(
    model_id,
    quantization_config=quant_config,
    device_map="auto",
)

Prepare the model for k-bit training, then apply the LoRA configuration with get_peft_model. The LoraConfig takes a target_modules list. For most decoder-only models this is the attention projection names. You can inspect them by printing the model and looking for Linear layers inside the attention blocks. Common targets are q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj.

from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training

model = prepare_model_for_kbit_training(model)

lora_config = LoraConfig(
    r=16,
    lora_alpha=32,
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM",
    target_modules=["q_proj", "v_proj"],
)

model = get_peft_model(model, lora_config)
model.print_trainable_parameters()

Then create an SFTTrainer from trl. Pass the LoRA-wrapped model, the training arguments, the dataset, and a formatting function that turns each example into a single string. The SFTConfig in the current version of TRL handles packing and the chat template internally if you pass a tokenizer with a chat template.

Your formatting function should create the exact prompt pattern you will use when testing the adapter. Here is a simple version for Alpaca-style records:

def format_example(example):
    prompt = f"### Instruction\n{example['instruction']}\n\n"
    if example.get("input"):
        prompt += f"### Input\n{example['input']}\n\n"
    prompt += f"### Response\n{example['output']}"
    return prompt

A minimal training arguments block looks like this. Per-device train batch size of 2, gradient accumulation steps of 4, learning rate of 2e-4, num_train_epochs of 3, warmup ratio of 0.03, logging steps of 10, save strategy of steps, save steps of 100, and a bf16 or fp16 flag matching your hardware.

Call trainer.train. On a hosted GPU with a 1B parameter model and a few thousand examples, this can finish in well under an hour. On a 7B model with 4-bit quantization, expect a longer run that depends heavily on sequence length, dataset size, and the GPU you are allocated.

After training, save the adapter with model.save_pretrained. To use it for inference, load the base model in the same way, then call PeftModel.from_pretrained with the adapter path. For production, call model.merge_and_unload and save the merged model, which removes the adapter overhead and gives you a single set of weights you can serve with vLLM, TGI, or llama.cpp.

Key settings that matter

Rank is the single most important knob. It is the r in the low-rank decomposition. Typical values are 4, 8, 16, 32, and 64. Lower rank means fewer trainable parameters, less memory, and faster training, but less expressive capacity. Higher rank lets the adapter represent more complex adaptations but starts to eat the memory savings. For most style and tone adaptations, 8 or 16 is plenty. For behavior shifts, you may want 32 or 64.

Alpha controls the scaling of the LoRA update. The effective update is alpha divided by rank times the product A times B. The common rule of thumb is to set alpha equal to 2 times rank, so the effective scale is 2. Setting alpha equal to rank gives a scale of 1, which is more conservative.

Target modules decide where in the model the adapters get injected. Attention-only targets (q_proj and v_proj) are the original recommendation from the LoRA paper and work well for many tasks. Adding the MLP projections (gate_proj, up_proj, down_proj) typically gives a quality bump at the cost of more trainable parameters and memory. For most decoder-only models in the current landscape, the all-linear convention is a solid default.

Learning rate for LoRA is much higher than full fine-tuning. Full fine-tuning of a 7B model usually uses a learning rate in the 1e-5 to 5e-5 range. LoRA training commonly uses 1e-4 to 3e-4. Going higher than that without a warmup usually destabilizes training.

Quantization dtype for QLoRA is the next decision. NF4 with double quantization is the default and a sensible starting point. FP4 is faster but slightly less accurate in practice. If you have a GPU with good bfloat16 support, you can also use 8-bit quantization for the base model, which costs more memory but trains more stably.

Sequence length matters more than most beginners expect. Long contexts multiply memory use in attention. On a T4 with a 7B model in 4-bit, a sequence length of 1024 is often close to the practical ceiling for batch size 1. You can trade batch size for sequence length with gradient accumulation, but you cannot escape the attention memory cost.

Number of epochs and dataset size are where the quality ceiling actually lives, not the LoRA settings themselves. A rank-8 adapter trained for many epochs on a well-curated thousand-example set will usually outperform a rank-64 adapter trained for one epoch on noisy data.

Common LoRA fine-tuning errors and fixes

CUDA out of memory: Reduce sequence length first. Then reduce per-device batch size to 1 and increase gradient accumulation to preserve the effective batch size. If that still fails, use a smaller base model or reduce the target modules to attention projections only.

Loss becomes NaN: Switch the compute dtype from bfloat16 to float16 if your allocated GPU does not properly support bfloat16. Reduce the learning rate to 1e-4, check for malformed records, and confirm that every example has a non-empty target output.

The adapter repeats the prompt: This is usually a data formatting issue. Make sure the response follows a clear separator, and verify whether your trainer should mask prompt tokens so the loss focuses on the answer.

The model ignores the adapter: Confirm that you loaded the same base model used during training. Check that the adapter path is correct and call model.print_trainable_parameters() before training to confirm LoRA layers were created.

The result is worse than the base model: Inspect the data before changing hyperparameters. Duplicated examples, conflicting instructions, low-quality outputs, and too many epochs can all damage a narrow adapter quickly.

Where it shines

LoRA is at its best when you need to teach a strong base model a narrow, well-defined skill. Examples that work well include teaching a model to respond in a specific format, to follow a particular writing style, to use domain terminology correctly, or to output structured data in a schema of your choice.

It is also excellent for tool use and function calling, where you are essentially teaching the model a new output grammar. The adapter is small, often tens of megabytes, so you can keep a library of them, one per tool or per customer, and swap them in and out at runtime.

Multilingual adaptation is another good fit. A base model that already knows a language can be nudged into cleaner, more idiomatic output in that language with a relatively small LoRA on a few thousand examples.

Personalization is the use case that gets the most attention. You can fine-tune a small adapter per user or per account, and because the base model is shared, you only pay the storage cost for the small adapter files. This is the architecture behind a lot of the “your own model” services in the current landscape.

LoRA also shines in research and experimentation. Because each training run is cheap, you can iterate on prompts, data mixes, and hyperparameters far more aggressively than you can with full fine-tuning. The feedback loop is short, which matters more than people expect.

Where it fails

LoRA cannot teach a model genuinely new factual knowledge at the scale most people imagine. The base model’s weights are frozen, and the adapter is too small to store more than a sliver of new information. If you try to fine-tune a model to “know” your entire 500-page internal knowledge base, you will get a model that hallucinates confidently in the shape of your documents.

Tasks that require large behavior changes are also a poor fit. If the base model refuses a class of requests and you want it to comply, LoRA will fight the base weights and usually lose. The adapter capacity is not enough to override a strong prior baked into the model.

Long-context reasoning is limited by the base model’s context length, not by LoRA. People sometimes blame LoRA for context failures when the underlying issue is the model running out of attention budget.

Inference quality at the high end still trails full fine-tuning, especially for tasks like complex math, multi-step reasoning, and code generation. The gap is smaller than it used to be, and a well-tuned QLoRA adapter can approach full fine-tuning on some narrow benchmarks, but the ceiling is real.

Free GPU tiers have their own failure modes. Colab will time out and disconnect you if your session sits idle or runs too long. Kaggle resets usage quotas. Lightning AI free credits can expire. For any production work, plan for a paid tier or a self-hosted box, because the free tiers are training environments, not reliable serving environments.

Merging adapters is not free of trade-offs. Once you merge a LoRA into the base model, you cannot easily unmerge it, and you cannot selectively disable it. For multi-adapter pipelines, keep adapters separate and load them at inference.

For a more candid view of the trade-offs, read Fine-Tuning Reality Check: What Practitioners Actually Found.

Practical workflow pattern

A realistic workflow looks like this. Start with a strong base model, usually one of the open 7B to 13B parameter chat models in the current landscape. Pick the smallest model that can plausibly do the task, because smaller models are faster to iterate on and easier to serve.

Curate a small, high-quality dataset. A few hundred to a few thousand examples is usually enough for a narrow skill. Quality matters more than quantity, and you should be ruthless about removing noise, duplicates, and examples that conflict with what you actually want.

Run a baseline evaluation before you train anything. Pick a small held-out set of inputs, generate outputs from the base model, and score them. This is your floor, and it tells you whether LoRA is moving the needle at all.

Train with conservative defaults. Rank 16, alpha 32, attention plus MLP targets, learning rate 2e-4, three epochs, sequence length 1024. Save checkpoints frequently so you can compare runs.

Evaluate after training on the same held-out set, plus a small set of edge cases and adversarial inputs. Look for regressions as well as improvements, because LoRA can make the model better at one thing and worse at something it used to do well.

If the adapter is good, decide between merging and keeping it separate. Merge if you are shipping a single-purpose model and want maximum inference speed. Keep separate if you want to swap adapters, A/B test, or roll back easily.

Store your training data, your LoRA config, your evaluation prompts, and your notes in version control. LoRA runs are cheap, but reproducibility is not free, and you will want to rerun this in three months.

For the “GPU free” angle specifically, treat the free tier as your development environment. Use it to iterate on data, prompts, and configurations. When you have a configuration that works, promote it to paid compute for the long training run and any production serving.

To see how tools like this fit into a complete operating layer for your business, book a 60-min Omni Audit: https://enterprisedna.co/omni/book/