Large Language Models in Finance · Chapter 3 / Lecture 3 · Practical

Training and Fine-Tuning Large Language Models

Hands-on: parameter counting, Chinchilla budgeting, instruction tuning, and FinBERT sentiment with PEFT.
Juan F. Imbet  ·  EDHEC Business School / Paris Dauphine – PSL University
Session overview · 1 hour

What we'll do in the next hour

Four skills, end to end: read a scaling law as a budget, size a LoRA adapter, run a real fine-tune on a finance task, and reason about preference data for alignment.

5'
Recap — LoRA · Chinchilla · CLM/MLM · Alignment pipeline
15'
Problem 1 — LoRA param counting (7B LLaMA)
10'
Problem 2 — Chinchilla compute budgeting
20'
Case study — FinBERT + LoRA fine-tune
5'
Discussion — which adaptation strategy, when?
5'
Solutions — Problems 1 & 2 walkthrough
what you will practise Turning scaling-law arithmetic into hardware decisions, choosing LoRA settings, running a real fine-tuning loop on a finance task, and reasoning about preference data for DPO alignment.
01

Recap — the formulas you'll lean on

LoRA's parameter count, Chinchilla's budget rule, CLM vs. MLM, and the alignment pipeline.
Recap · the training pipeline

Three stages from raw text to aligned model

Every modern finance LLM passes through three distinct phases — understanding which phase you are in determines which tools and metrics matter.

Training pipeline: pre-training to SFT to RLHF
Figure. Pre-training instils language and world knowledge; SFT teaches instruction-following; RLHF (or DPO) aligns the policy to human preferences. In finance, this pipeline terminates with domain-adaptive pre-training and task-specific fine-tuning before deployment. Source: course illustration.
Recap · adapt without retraining

LoRA: train a tiny patch, not the whole model

Instead of editing billions of frozen weights, LoRA bolts on two small matrices and only trains those — a cheap, removable "patch."

  • The base weights stay frozen; only the patch learns
  • Start the patch at zero, so day-one behaviour is unchanged
  • At inference you can merge the patch in — zero extra cost
  • Rank \(r\) is the dial: typical 4, 8, 16, 32
you'll need this for Problem 1 Summing \(r(d+k)\) over every adapted layer gives the total trainable parameter count.
Recap · how big, on how much data

Chinchilla: model size and data should grow together

For a fixed compute budget there's a sweet spot — roughly 20 tokens of training data per model parameter. Too big a model on too little data wastes the budget.

Pre-training objectives at a glance
ObjectiveArchitectureFinance use
CLM (causal)Decoder-onlyGeneration, chat, BloombergGPT
MLM (masked)Encoder-onlyClassification, NER, FinBERT
Span corruptionEncoder-decoderQA, summarisation
you'll need this for Problem 2 \(N^* = \sqrt{C/120}\) is your main tool for Chinchilla budgeting.
02

Problems — do the arithmetic yourself

Count LoRA parameters on a 7B model, then size a model to a compute budget.
Problem 1 · setup

How many parameters does a LoRA patch actually train?

Model: LLaMA-7B, 32 transformer layers, hidden dimension \(d = 4096\). Each layer has four square projection matrices.

The four projections (each \(4096 \times 4096\))
  • Query \(W_Q\), Key \(W_K\)
  • Value \(W_V\), Output \(W_O\)

Task: apply LoRA with rank \(r = 4\) to the Q and V projections only.

Fill in (one layer)
Matrix\(d\)\(k\)\(r\)
\(W_Q\)???
\(W_V\)???
hint Per adapted matrix the trainable count is \(r(d+k)\). Sum over both matrices and all 32 layers.
Problem 1 · tasks · 15 min

Four questions on the LoRA patch

Using the setup (\(r=4\), Q and V only, 32 layers, \(d=k=4096\)) — work individually or in pairs.

  1. A. Compute the total trainable LoRA parameters. Show the step-by-step arithmetic.
  2. B. Express this as a fraction of the 7B total. Is it above or below 0.1%?
  3. C. Double the rank to \(r = 8\). By what factor does the count change — linear, quadratic, or something else? Justify.
  4. D. A colleague prefers older adapter modules. Give one concrete reason LoRA wins for inference-time deployment on a high-throughput trading floor where latency is measured in milliseconds.
Problem 2 · setup

Given a compute budget, how big should the model be?

Scenario: an investment bank's AI team has a compute budget of \(C = 10^{22}\) FLOPs to train a financial language model.

Tools
Use \(C \approx 6ND\), \(D^* \approx 20 N^*\), and therefore \(N^* = \sqrt{C/120}\).
Task A
  1. Compute \(N^*\) and \(D^*\).
  2. The team has a 50B-token financial corpus. Are they data-constrained or compute-constrained? (Compare \(D^*\) to 50B.)
  3. What should they do given that diagnosis?
Problem 2 · continued · 10 min

Pushing the budget further

  1. B. The team instead trains a 1B-parameter model. By the Chinchilla rule, how many tokens for a compute-optimal run? What is the implied FLOP cost?
  2. C. A senior researcher argues "train the biggest model possible to maximise accuracy." When is this valid? When is it wrong?
frame your answer around Held-out loss, inference serving cost, and whether a bigger model would end up under-trained for the budget.
03

Case study — fine-tune FinBERT with LoRA

Load a pre-trained finance model, attach a LoRA adapter, train, and read the diagnostics.
Case study · the goal

Teach a finance model with a tiny removable patch

Load pre-trained FinBERT and apply LoRA fine-tuning on a handful of labelled financial sentences using HuggingFace PEFT.

The stack
  • transformers — load ProsusAI/finbert
  • peftLoraConfig, then get_peft_model
  • torch — a hand-written training loop (no Trainer, for transparency)
the bigger picture The dataset is intentionally tiny — 5 sentences. The point is to see the mechanics (how few parameters move), not to maximise accuracy. FinBERT has 110M parameters; the LoRA patch will be a fraction of 1%.
Case study · what you'll do

Three steps, then three diagnostic questions

What you will do
  1. Run the code on the next frames
  2. Observe how many parameters are trainable vs. frozen
  3. Answer the diagnostic questions that follow
prerequisites pip install transformers peft torch — no API key required for this case study.
Case study · code 1a — imports + data

Five labelled sentences (positive / negative / neutral)

import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification
from peft import LoraConfig, get_peft_model, TaskType

MODEL = "ProsusAI/finbert"
SENTENCES = [
    ("Revenue increased 15% year-over-year.", 0),         # positive
    ("The firm reported a net loss of 200M USD.", 1),     # negative
    ("Operating margins remained broadly stable.", 2),    # neutral
    ("Strong demand drove record quarterly earnings.", 0),# positive
    ("Guidance was withdrawn amid macro uncertainty.", 1),# negative
]
Case study · code 1b — load + LoRA config

Attach the adapter to query and value only

tokenizer = AutoTokenizer.from_pretrained(MODEL)
model = AutoModelForSequenceClassification.from_pretrained(MODEL, num_labels=3)

lora_cfg = LoraConfig(
    task_type=TaskType.SEQ_CLS,
    r=8,
    lora_alpha=16,
    target_modules=["query", "value"],
    lora_dropout=0.05,
    bias="none",
)
model = get_peft_model(model, lora_cfg)
model.print_trainable_parameters()  # observe trainable % here
watch this line print_trainable_parameters() is the whole lesson: a sub-1% slice of the model is learning.
Case study · code 2 — train + eval

Three epochs, then read the predictions

texts, labels = zip(*SENTENCES)
enc = tokenizer(list(texts), padding=True, truncation=True, return_tensors="pt")
label_tensor = torch.tensor(labels)

optimizer = torch.optim.AdamW(model.parameters(), lr=2e-4)
model.train()
for epoch in range(3):
    out = model(**enc, labels=label_tensor)
    out.loss.backward(); optimizer.step(); optimizer.zero_grad()
    print(f"Epoch {epoch+1}: loss={out.loss.item():.4f}")

model.eval()
with torch.no_grad():
    logits = model(**enc).logits
    preds = logits.argmax(-1)
    id2label = {0: "positive", 1: "negative", 2: "neutral"}
    for sent, pred in zip(texts, preds):
        print(f"[{id2label[pred.item()]}] {sent}")
Case study · diagnostics 1 & 2

Does the math match what the code reports?

  1. Q1 — trainable fraction. Compare the reported trainable count to total FinBERT size (\(\approx 110\)M). How does it line up with the theoretical value for LoRA \(r=8\) on query and value across all 12 BERT layers?
  2. Q2 — rank sweep. Change r=8 to r=4 then r=16; rerun the loop each time. Do predictions change? Does loss fall faster at higher rank? What does that say about rank vs. expressiveness on a tiny dataset?
Case study · diagnostic 3

Is patching all four projections worth the extra cost?

Q3. Change target_modules to all four projections: ["query", "key", "value", "dense"]. How does the trainable count change? Do the predictions improve? Is the extra compute justified?

Optional extension
Swap the 5 sentences for 50 examples from the Financial PhraseBank (HuggingFace takala/financial_phrasebank). Evaluate accuracy on a held-out 20% split at \(r = 4, 8, 16\), and plot accuracy vs. trainable parameter count.
04

Discussion — which adaptation strategy, and why?

Match the tool to the constraints: data, compute, latency, privacy, cost, explainability.
Discussion · 5 min

Pick the right tool for each desk

For each scenario, choose: zero-shot / few-shot ICL / LoRA fine-tune / full fine-tune — and say why.

  1. Hedge fund. Classify 10-K risk-factor sections "material" vs. "immaterial" for one covenant type. 50 labelled examples, 2-hour deadline, using GPT-5.6 Luna via API.
  2. Tier-1 bank. On-premise earnings-call sentiment (no external API), 200,000 docs/month, GDPR data-residency, 20,000 labelled examples.
  3. Fintech startup. General-purpose financial chatbot; no task-specific labelled data, but access to a pre-trained instruction-tuned LLM.
  4. Quant research. Test whether a 70B model can replicate an expert's reasoning on novel bond-structuring questions. No labelled data exists.
frame your answer around Labelled-data volume, compute budget, latency, data-privacy constraints, inference cost, and desired explainability.
05

Solutions — the worked answers

Problem 1 parameter count and Problem 2 budget, step by step.
Solution · Problem 1

A 7B model fine-tuned by moving ~2 million parameters

Filled table (one layer, \(r=4\), Q and V, \(d=k=4096\))
Matrix\(d\)\(k\)\(r\)
\(W_Q\)409640964
\(W_V\)409640964
Task D — why LoRA for the trading floor LoRA merges \(W_0 + \tfrac{\alpha}{r}BA\) into one weight before deployment, so the served model has the base model's exact structure. Classic adapters add a sequential bottleneck that cannot be merged away — extra latency per token, unacceptable in millisecond-sensitive trading.
Solution · Problem 2 + wrap-up

The budget says: build small, train fully

session summary LoRA's parameter count is linear in \(r\), and merging removes inference overhead. Chinchilla: \(D^*/N^* \approx 20\), and most realistic settings are compute-constrained. Next practical: fine-tuning a generative model for financial QA with LoRA, and building preference pairs for DPO.
A

Appendix — stretch problems (optional)

Instruction tuning arithmetic, and aligning a generative model with Direct Preference Optimisation.
Appendix · stretch · 10 min (take-home)

Instruction tuning: why format diversity matters

PIXIU / FinMA (Xie et al., 2023) trained on 136,000 financial instruction samples. InvestLM used far fewer but carefully curated samples. Both achieved strong results — for very different reasons.

  1. A. The Superficial Alignment Hypothesis says instruction quality matters more than quantity. Design a test: what would you measure on a held-out set to distinguish a quality effect from a quantity effect?
  2. B. A financial chatbot needs to handle three phrasings of the same task: "What is the sentiment?", "Classify as positive/negative/neutral:", "Rate the tone of:". How would you structure an instruction-tuning dataset to handle all three without labelling 3× as much data?
  3. C. The information-overload effect (Balogh & Didisheim, 2025) shows an inverted-U: accuracy peaks then falls as context length grows. How would you operationalise this finding when designing a RAG pipeline for earnings-call QA?
Appendix · stretch · 15 min (take-home)

DPO: aligning a model from preference pairs

You have the LoRA-fine-tuned FinBERT sentiment model from the case study. Now align a small generative finance model with Direct Preference Optimisation on preference pairs \((x, y_w, y_l)\).

  1. A. In one sentence: what does raising \(\beta\) do to how far \(\pi_\theta\) may drift from \(\pi_{\mathrm{ref}}\)?
  2. B. Why does DPO need a frozen reference policy \(\pi_{\mathrm{ref}}\) at all? What failure occurs if you drop it?
  3. C. Construct three plausible \((y_w \succ y_l)\) preference pairs for a financial-advice prompt where "plausible vs. accurate" is the distinguishing axis. Annotate each with the relevant regulatory concern (MiFID II suitability, SR 11-7, or hallucination).