
LLMs from Scratch & Medical Fine-Tuning
Implemented a GPT-2 architecture from scratch in PyTorch, then fine-tuned Mistral models on medical conversational and MCQ datasets using LoRA for clinical question answering.
- ▸Implemented full GPT-2 decoder-only transformer from scratch including multi-head attention with causal masking, learned positional embeddings, and layer normalization
- ▸Fine-tuned Mistral-7B and TinyMistral-248M on 105K medical conversations and 182K multiple-choice examples (MedMCQA) using LoRA with 8-bit quantization
- ▸Built containerized training pipeline deployed on EPFL's Run:AI GPU cluster with multi-GPU support via DeepSpeed
- ▸Developed instruction-following fine-tuning with loss masking on prompt tokens for targeted response learning
Overview
Grade: 5.75 / 6 | EPFL semester project (12 ECTS), supervised by Yousra El-Bachir and Oleg Bakhteev under Prof. Mathieu Salzmann
This was an EPFL semester project completed with Ali Ridha Mrad and split into two phases: first implement a GPT-2 style language model entirely from scratch to understand transformer internals, then apply efficient fine-tuning techniques to adapt larger models for the medical domain.
The code is split across two repos: the base LLM implementation and the medical fine-tuning pipeline.
What I Built
1. GPT-2 Architecture from Scratch

Following Sebastian Raschka's "Build a Large Language Model from Scratch," I implemented the full decoder-only transformer in PyTorch:
- Multi-head self-attention with causal masking
- Learnable token and positional embeddings
- Feed-forward networks with GELU activation
- Residual connections and layer normalization
- Support for GPT-2 Small (124M params) and Medium (355M params)
Tokenization uses OpenAI's tiktoken BPE tokenizer. The model supports loading official GPT-2 pretrained weights and autoregressive text generation.
I then fine-tuned this for two downstream tasks: spam classification (with a linear classification head) and instruction following (using structured prompt formatting with loss computed only on response tokens).
2. Medical Fine-tuning with LoRA
For the medical domain, I fine-tuned Mistral-7B and TinyMistral-248M using parameter-efficient LoRA adapters rather than updating all weights:
- LoRA config: rank 8, alpha 16, dropout 0.05, targeting Q/K/V/O projection layers
- Quantization: 8-bit loading via BitsAndBytes for memory efficiency
- Data: 105,488 medical conversations combined with 182,822 multiple-choice examples (MedMCQA)
- Prompt format:
<s>[INST] instruction [/INST] response </s>with loss computed only on the response tokens (instruction tokens masked out) - Infrastructure: Dockerized on NVIDIA PyTorch base image (CUDA 12.6), deployed to EPFL's Run:AI cluster with multi-GPU support via DeepSpeed
Technical Details
Base model (from-scratch implementation):
- Architecture: Decoder-only transformer (GPT-2)
- Parameters: 124M (Small) / 355M (Medium)
- Attention heads: 12 (Small) / 16 (Medium)
- Hidden dimension: 768 (Small) / 1024 (Medium)
- Layers: 12 (Small) / 24 (Medium)
Medical fine-tuning:
- Base models: Mistral-7B, TinyMistral-248M
- Method: LoRA with 8-bit quantization
- Training data: 105,488 medical conversations (instruction-response pairs) + 182,822 multiple-choice examples (MedMCQA)
- Compute: EPFL's Run:AI cluster (NVIDIA GPUs), with gradient accumulation and configurable hyperparameters
Challenges & Tradeoffs
- Full fine-tuning vs. LoRA: With limited cluster compute, LoRA made it feasible to fine-tune 7B-parameter models by only training a small number of adapter parameters
- Quantization trade-offs: 8-bit quantization reduced memory requirements significantly but required careful handling (Flash Attention 2 caused instability on A100s, so standard attention was used)
- Loss masking: Computing loss only on response tokens (not instruction/prompt tokens) was important for teaching the model to generate useful responses rather than memorizing prompts
Results
The fine-tuned models produce coherent medical responses to patient queries, handling topics like symptom assessment, medication information, and general health guidance. The focus of this project was the from-scratch implementation and the training infrastructure, so I validated the fine-tuned models qualitatively rather than against a held-out benchmark. Below are two examples of the model in action:


What I Learned
- Implementing attention from scratch gave me a mechanical understanding of transformers that using a library does not
- LoRA is effective: adapting a 7B model's behavior only requires training less than 1% of its parameters
- Infrastructure matters: containerizing the training pipeline and configuring cluster jobs (Run:AI, NAS storage, GPU scheduling) was a significant part of the real work
- Loss masking on prompt tokens is a small implementation detail that makes a big difference in fine-tuning quality