How does an LLM really work?

A recap of the conversation — from the core mechanism through training and tokenization to a hands-on fine-tuning example and deployment.

SCC Informationssysteme GmbH · August 8, 2026
Fundamentals

The core principle: predicting text

An LLM always predicts exactly one thing: which token (a word or part of a word) is most likely to come next. This prediction is repeated token by token until a complete response emerges.

Prompt + tokens so far Model (weights) Probabilities Paris Berlin and … approx. 50,000 more Pick token: "Paris" appended → next step

Each step produces a probability over all ~50,000 possible tokens; the most likely one is appended, then the cycle begins again.

Important: no text is "looked up." Every single token is recomputed freshly on each request — based on the trained weights, not from a stored archive.
Training

How training works: the learning cycle

For the weights to be able to compute these probabilities sensibly, they first have to be trained. That happens in a constantly repeating, four-step cycle:

1. Prediction Forward pass 2. Measure error Loss 3. Trace back Backpropagation 4. Adjustment Gradient descent billions of repetitions

The training process in outline

Training means: the weights themselves are changed step by step, based on a recurring four-step cycle.

Step one, the prediction (forward pass): You give the model a piece of text, for example "The sky is," and have it predict which word comes next. With the current (initially random) weights, this produces some probability distribution, at first usually complete nonsense.

Step two, the error (loss): From the real training text you know the actual next word (here, for example, "blue"). You compare the model's prediction with this real word and compute a numeric value for how wrong the prediction was — this is called the loss.

Step three, tracing back (backpropagation): Now comes the actual mathematical work. The procedure calculates, for each of the billions of weights in the network, how strongly and in which direction that one weight contributed to the measured error — by systematically computing backward through the network, hence the name.

Step four, the adjustment (gradient descent): Based on this information, each weight is nudged a small amount in the direction that would make the error a bit smaller next time — just a small step, not the full correction all at once.

No human prescribes "correct answers" here: in the first training phase, the training text itself supplies the solution — the next word is hidden and the model has to predict it. This is called self-supervised learning, because the data essentially corrects itself, without a human having to evaluate every single answer in advance.

Training

The three training phases

A finished chat model like this one doesn't come into being in a single step, but through three successive phases:

Pretraining Billions of texts Next-word prediction → language understanding Supervised Fine-Tuning Human-reviewed question/answer examples → dialogue ability RLHF Humans rate multiple answers → helpful & safe
Model quality

Parameters (weights) vs. data quality

The number of weights (parameters) determines a model's capacity — how many patterns it could theoretically represent. Whether this capacity is used well depends on the quality of the training data.

Small model, good data little space, but filled entirely with high quality Large model, bad data lots of space, but filled with errors/nonsense

More weights = more capacity (the shelf is bigger). But what determines whether the result is good is what gets put on it — not how much space is available.

Other factors besides data quality: training method, the ratio of model size to data volume (Chinchilla scaling laws), the fine-tuning phase, and the network architecture itself.
Hardware

Why graphics cards (GPUs)?

The math of neural networks consists almost entirely of matrix multiplications — many small, mutually independent computations that can run simultaneously instead of one after another. That's exactly what GPUs were built for.

CPU: few, powerful cores e.g. 8–64 cores, strong at sequential work GPU: thousands of simple cores e.g. 10,000+ cores, massively parallel

Because the same computation (multiply + add) has to happen millions of times in parallel for every weight simultaneously, a GPU is often up to a hundred times faster here than a CPU — the original purpose (rendering images, many pixels at once) happens to fit ideally with training neural networks.

Tokenization

Tokenization (BPE) in detail

Before a sentence is processed, it's broken down into tokens — building blocks from a fixed, pre-built list (the vocabulary, usually 30,000–100,000 entries). The technique behind this is called Byte Pair Encoding (BPE).

Live example from our conversation

Using a small, self-trained BPE tokenizer (training text: a few sentences about capital cities), the sentence "What is the capital of France" was split like this:

capital capita l</w> → only 2 tokens (frequent in training → strongly merged) France f r an c e </w> → 6 tokens (rare in training → barely merged) With a real tokenizer trained on billions of words, both words would likely be almost complete, single tokens — but the principle stays the same.
Frequency in the training text determines the granularity of the split — not meaning or grammar. As a fallback, individual letters/bytes always exist too, so any string can be tokenized, including cryptic or brand-new words.

New words don't automatically become new tokens

The token list is fixed once and doesn't grow automatically afterward. New words are assembled from existing smaller building blocks; the "knowledge" about them arises in the weights, not as a new vocabulary entry.

Tokenization

Tokens, embeddings and weights

Only the very first step can be shown concretely: every token has a fixed ID, which corresponds to a row in the so-called embedding table (part of the weights). Everything after that is spread across billions of shared weights and can no longer be attributed to a single token or concept.

"Paris" ID 14582 Embedding row [0.42, -0.11, …] shown concretely ✓ Transformer layers Attention + feed-forward billions of shared weights not attributable to one concept ✗ → the "interpretability" research field
Post-training

Catastrophic forgetting

At a fixed model size, all capacity is eventually "spoken for." If you train a finished model heavily on new content, it can lose old capabilities.

Accuracy Training steps → old capability new capability optimal stopping point

This is measured with fixed benchmark test sets, compared before and after post-training — not by repeatedly asking the same question.

Countermeasures used in practice

TechniqueEffect
Replay / rehearsalkeep mixing in old training data
Low learning rateonly gentle, small weight adjustments
LoRA / adaptersbase weights stay completely frozen
Elastic Weight Consolidationimportant old weights are specifically protected
Fine-tuning

LoRA: efficient fine-tuning

Instead of all the billions of weights, LoRA only trains small, additional weight matrices. The base model stays completely frozen.

Base model Billions of weights 🔒 frozen ~99.7 % of all weights LoRA adapter small, trainable (~0.3 %) only these weights change during training

Advantage: low computational cost (often a single GPU instead of a data center) and low risk of catastrophic forgetting, because the original stays untouched. With QLoRA, the base model is additionally quantized to 4-bit to save memory.

Hands-on example

From customer conversations to a training file

The complete practical path we walked through:

Raw chats / emails Curate & filter JSONL file (instruction/output) finetune.py (LoRA training) Your own model

Training example (format as in training_data_example.jsonl):

{"instruction": "A customer asks: 'Does your software also support the DATEV interface?'", "input": "", "output": "Yes, our software has a certified DATEV interface …"}

The training script uses transformers, peft (LoRA) and trl, and runs through exactly the learning cycle described above for each example. Rule of thumb: a few hundred to a few thousand carefully reviewed examples are usually enough for noticeable improvements.

Hands-on example

The complete training code

The complete, commented finetune.py script from our conversation — uses transformers, peft (LoRA/QLoRA) and trl, loads the base model in 4-bit, configures LoRA, trains with training_data_example.jsonl and tests the result directly at the end.

"""
Example script: fine-tuning an open LLM (e.g. Mistral-7B or Llama-3-8B)
with LoRA (parameter-efficient fine-tuning) for a company-specific
use case (here: IT support / customer communication for a software company).

Required libraries (see README.md for installation):
    pip install transformers peft trl datasets accelerate bitsandbytes torch

Prerequisite: a GPU with sufficient VRAM (see README.md for guidance).
For a 7B model with 4-bit quantization (QLoRA), 16-24 GB VRAM is often
enough (e.g. an RTX 4090 or a cloud GPU like A10/A100).

This script is deliberately kept simple to show the basic flow. For
production projects you should additionally add validation splits,
logging (e.g. Weights & Biases) and hyperparameter search.
"""

import torch
from datasets import load_dataset
from transformers import (
    AutoModelForCausalLM,
    AutoTokenizer,
    BitsAndBytesConfig,
    TrainingArguments,
)
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
from trl import SFTTrainer, SFTConfig

# ---------------------------------------------------------------------------
# 1. Choose base model
# ---------------------------------------------------------------------------
# A freely available, open model from Hugging Face. For real-world use
# you may have to request access from the provider once (e.g. Meta for
# Llama); Mistral models are usually usable without approval.
BASE_MODEL = "mistralai/Mistral-7B-Instruct-v0.3"

# ---------------------------------------------------------------------------
# 2. Load model in 4-bit (QLoRA) -> massively saves GPU memory
# ---------------------------------------------------------------------------
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16,
    bnb_4bit_use_double_quant=True,
)

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

model = AutoModelForCausalLM.from_pretrained(
    BASE_MODEL,
    quantization_config=bnb_config,
    device_map="auto",
)
model = prepare_model_for_kbit_training(model)

# ---------------------------------------------------------------------------
# 3. Configure LoRA
# ---------------------------------------------------------------------------
# Instead of all the billions of weights, only small, additional
# weight matrices are trained ("r" determines their size/capacity).
# The original weights stay frozen -> low risk of catastrophic
# forgetting, low computational cost.
lora_config = LoraConfig(
    r=16,
    lora_alpha=32,
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM",
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# Example output: "trainable params: 20,971,520 || all params: 7,262,703,616
#                   || trainable%: 0.29 %"
# -> Only about 0.3 % of all weights are actually changed.

# ---------------------------------------------------------------------------
# 4. Load training data
# ---------------------------------------------------------------------------
# Expected format: a JSON Lines file, one line = one training example
# with the fields "instruction", "input" (optional) and "output".
# See training_data_example.jsonl for a concrete example.
dataset = load_dataset("json", data_files="training_data_example.jsonl", split="train")


def format_prompt(example):
    """Turns a training example into the model's chat/prompt format."""
    if example.get("input"):
        prompt = f"{example['instruction']}\n\n{example['input']}"
    else:
        prompt = example["instruction"]

    text = (
        f"<s>[INST] {prompt} [/INST] {example['output']}</s>"
    )
    return {"text": text}


dataset = dataset.map(format_prompt)

# ---------------------------------------------------------------------------
# 5. Training configuration
# ---------------------------------------------------------------------------
sft_config = SFTConfig(
    output_dir="./result-finetuned-model",
    num_train_epochs=3,
    per_device_train_batch_size=2,
    gradient_accumulation_steps=4,
    learning_rate=2e-4,          # deliberately low, see the chapter on
                                  # catastrophic forgetting in the conversation
    logging_steps=5,
    save_strategy="epoch",
    bf16=True,
    max_seq_length=1024,
    dataset_text_field="text",
    report_to="none",            # alternatively e.g. "wandb" for a logging dashboard
)

# ---------------------------------------------------------------------------
# 6. Start training
# ---------------------------------------------------------------------------
trainer = SFTTrainer(
    model=model,
    args=sft_config,
    train_dataset=dataset,
)

trainer.train()

# ---------------------------------------------------------------------------
# 7. Save result (only the small LoRA weights, a few MB)
# ---------------------------------------------------------------------------
trainer.save_model("./result-finetuned-model")
tokenizer.save_pretrained("./result-finetuned-model")

print("Done. The fine-tuned model (LoRA adapter) is located at "
      "./result-finetuned-model")

# ---------------------------------------------------------------------------
# 8. Quick test after training
# ---------------------------------------------------------------------------
question = "A customer asks: 'What does an additional user license cost?'"
input_ids = tokenizer(f"<s>[INST] {question} [/INST]", return_tensors="pt").to(model.device)
output = model.generate(**input_ids, max_new_tokens=150)
print(tokenizer.decode(output[0], skip_special_tokens=True))
Hands-on example

Templating instead of manual data entry

Recurring sentence structures ("Do you support X?") don't have to be duplicated by hand. A template plus a list of topics/facts generates the JSONL lines automatically.

Topic list • DATEV → Fact A • Data protection officer → Fact B • Student internship → Fact C + 3-5 question phrasings per topic generate_ training_data.py training_data_generated.jsonl 10 finished lines from 3 topics × phrasings Answers (facts) still need to be reviewed/entered by humans

Special case: business-critical ambiguity

You don't need to artificially insert general typos — the base model's pretraining already covers that. What's worth doing deliberately, though, is targeting typos that happen to form another real word (e.g. "DATE interface" instead of "DATEV interface") — a specifically added training example helps avoid genuine misunderstandings here.

Hardware

Memory requirements & quantization

A model doesn't necessarily need to fit entirely into GPU memory. device_map="auto" distributes it intelligently:

GPU memory fast most layers RAM (CPU) medium speed remaining layers Disk slow last resort Only if all three combined aren't enough: an explicit "out of memory" error

Other levers: stronger quantization (4-bit, 3-bit), a smaller model, gradient checkpointing, smaller batch size/sequence length. For weaker laptops, tools like Unsloth (significantly lower memory footprint) are also useful, or on Apple Silicon machines, MLX/llama.cpp with Metal support.

Architecture decision

RAG vs. fine-tuning

RAGFine-tuning
Changes the weights?NoYes
Good forcurrent, frequently changing factstone, format, behavior
Effortlow (vector database + context)higher (training data + GPU)
Forgetting risknone (model stays unchanged)present, but reducible via LoRA

In practice, often combined: a lightly fine-tuned model for style and behavior, plus RAG for current, company-specific facts.

The key difference from RAG, summarized again

With real training, the weights themselves change permanently, through the cycle of predicting, measuring error, and adjusting, repeated over enormous amounts of data and enormous compute time (for large models this takes weeks to months on thousands of specialized chips). With RAG, the model stays exactly as it is — it merely gets additional text supplied at the runtime of a single request, which is immediately "forgotten" again after answering the question, because nothing about the weights was changed. That's why RAG is so well suited for current, constantly changing information, while real training is better suited for fundamental, stable language and factual understanding.

Architecture decision

Loading locally vs. an API connection

Unlike with a database, there is no connection string when loading a model locally — the weights are loaded once, completely, into your own working memory. With an API connection (e.g. using a cloud provider), the database comparison does apply, though.

Loading locally (from_pretrained)API usage
Where do the computations run?on your own hardwareat the provider
Network connection during use?noneyes, per request
Access to the weights?yes → fine-tuning possibleno (except via special fine-tuning APIs)
Comparable toloading a large file into memorya database connection (connection string / API key)
Deployment

Ollama & the source/build/deploy analogy

Ollama is meant for efficiently running finished models (format: GGUF), not for training them. The workflow maps almost one-to-one onto building and deploying a Java application:

Source HF base model + LoRA adapter (maintained) Build Merge + convert to GGUF (llama.cpp) Deploy Import into Ollama convenient to run
Important: there's no way back. GGUF is quantized and practically not losslessly reversible (comparable to repeatedly re-saving a JPEG). So: always keep maintaining the source copy (HF format) and rebuild to GGUF and redeploy at every step forward — never read back from Ollama.
Robustness

Why the output is still correct

Training data contains many typos and colloquial language — yet the model usually answers correctly. Three reasons:

1Statistical majority: the correct spelling is always the same, while errors are spread across many different, rare variants.
2Weighting good sources: edited texts (books, professional articles) are weighted more heavily during training.
3The fine-tuning phase: target answers in the training examples are always cleanly formulated — regardless of the quality of the question.
Model development

How user conversations feed into new models

A common but inaccurate assumption: "The provider analyzes how the model has changed through use." That's technically not correct — an already deployed LLM does not change on its own through use; the weights stay frozen, as already explained in the section on loading locally.

What actually gets collected: pure raw data, no "changed model"

What gets collected (depending on privacy settings and terms of use) is the conversation text itself — input and output as plain text, completely separate from the model and its weights. The model itself is not examined or "dissected" in the process; it's simply recorded what was asked and answered — exactly the same kind of raw material as in the original training, just real conversation transcripts instead of general internet text.

The selection process happens at the data level, not on the model

The curation (which conversations are good enough quality, where does the model show recognizable weaknesses, which topics are underrepresented, are there duplicates) happens at the text level, before any new training even begins. Only afterward does this curated selection feed into a new, deliberate training run (additional pretraining material, supervised fine-tuning, or RLHF) — with the same prediction/error/adjustment cycle as right at the beginning. The result is a completely new, standalone model version with newly computed weights, not a version of the model you were just talking to that "grew" in the background.

Economically, this is still a legitimate interest for providers: real user queries show precisely where people need help and where a model is still weak — often more valuable than randomly collected internet text. Whether and how this is allowed in any given case is governed by the respective provider's terms of use and privacy settings.
Conclusion

The key takeaways at a glance

Core principleToken-by-token probability prediction, no text archive
TrainingPrediction → error → backpropagation → adjust weights, billions of times
QualityParameter count = capacity; data quality determines what comes of it
TokenizationFixed building-block list (BPE), frequency determines granularity
Knowledge in the modelspread across billions of weights, not localizable
Fine-tuning yourselfLoRA/QLoRA on open models, with curated JSONL data
Current factsRAG rather than constant retraining
Deploymentmaintain the source (HF/LoRA), rebuild to GGUF for Ollama each time
Glossary

All abbreviations at a glance

For quick reference: every technical term and abbreviation used in this document, sorted alphabetically.

AbbreviationMeaning
APIApplication Programming Interface — an interface for using a system (e.g. an LLM) without running the software locally; the request goes out over the internet, and the response comes back from the provider's server.
BPEByte Pair Encoding — a tokenization method that merges text step by step, based on frequency, into a fixed list of word-building blocks (tokens); the basis of modern tokenizers.
CPUCentral Processing Unit — a computer's main processor; few but highly versatile cores, good for sequential tasks but poorly suited to the massively parallel matrix computations involved in LLM training.
EWCElastic Weight Consolidation — a technique against catastrophic forgetting: weights important for already-learned knowledge are specifically "protected" and changed less during further training.
GGUFGPT-Generated Unified Format — a compressed, quantized file format for model weights, used by llama.cpp and, building on that, Ollama, for efficient running (not training).
GPUGraphics Processing Unit — a graphics card; has thousands of smaller cores that can work simultaneously — ideal for the massively parallel matrix multiplications in training and running LLMs.
HFHugging Face — a platform and library ecosystem (including transformers, peft, trl, datasets) for downloading, training and sharing models; "safetensors" is the de facto standard format for training purposes.
JSONLJSON Lines — a text format where each line of the file is a standalone, complete JSON object (instead of one large JSON array); a common format for training datasets in fine-tuning.
LLMLarge Language Model — a large language model; predicts text token by token based on learned probabilities, without storing text as such.
LoRALow-Rank Adaptation — a fine-tuning method: the original model weights stay frozen, while small additional low-rank matrices ("adapters") are trained instead — only a fraction of the original parameters.
NF44-bit NormalFloat — a quantization format that compresses weights to 4 bits, specifically tailored to the typical distribution of model weights; a core component of QLoRA.
PEFTParameter-Efficient Fine-Tuning — an umbrella term for fine-tuning methods (including LoRA) where only a small portion of the model's parameters is trained, instead of all the weights; also the name of the Hugging Face library of the same name.
QLoRAQuantized LoRA — a combination of 4-bit quantization of the frozen base model (see NF4) and LoRA adapters; enables fine-tuning of large models on hardware with significantly less GPU memory.
RAGRetrieval-Augmented Generation — a technique where matching text passages (e.g. from a vector database) are looked up at runtime and supplied to the model as context; it does not change the weights.
RLHFReinforcement Learning from Human Feedback — a training phase in which a model is further refined based on human ratings (which answer is better?), usually after SFT.
SFTSupervised Fine-Tuning — the training phase after pretraining, in which the model learns to answer in a desired format (e.g. as a helpful assistant) using concrete example question/answer pairs.
VRAMVideo RAM — a graphics card's memory; limits how large a model (or how large a quantized version of it) can be loaded and trained on that GPU.

Note on brand and product names: The product, company and brand names mentioned in this document — including DATEV, Ollama, Meta Llama, Mistral AI, Hugging Face, OpenAI/GPT, Python, Java, Microsoft PowerPoint, Google Colab, RunPod, Apple/MLX — are trademarks or registered trademarks of their respective rights holders. They are used here solely for illustrative and explanatory purposes; no affiliation, endorsement, or partnership with the respective companies is implied.