Training a French GPT model from scratch on local hardware with full monitoring, periodic checkpoints, and conversational fine-tuning.
🔗 Hugging Face Model: vincent-pro-ai/french-llm-from-scratch
This project demonstrates the complete pipeline for training a medium-sized GPT model for French language from scratch, running entirely on consumer hardware (RTX 5080, 16GB VRAM).
Key Achievements:
Complete Training Pipeline (60k → 250k steps)
| Checkpoint | Steps | Val Loss | Improvement | Details |
|---|---|---|---|---|
| Baseline | 60k | 7.13 | - | Initial training |
| First milestone | 115k | 4.88 | -31.6% | +15M conversation tokens |
| Scaled dataset | 180k | 4.89 | +0.2% | 197M tokens corpus |
| Final base model | 200k | 4.79 | -2% | High-quality base |
| DDP Validation | 250.5k | 3.99 | -16% | Multi-GPU stable training |
Hardware Setup:
torchrunExpanding beyond training from scratch, we've implemented a professional fine-tuning pipeline for Mistral-7B-v0.3 using QLoRA on our Multi-GPU infrastructure.
Highlights:
bfloat16 and optimized learning rate (5e-5) to avoid loss divergence.Why this matters: This demonstrates the ability to take state-of-the-art weights and customize them for French specificities on local hardware, while ensuring the result is immediately deployable.
Dataset Evolution:
Training Metrics:
Qualitative Improvements:
Model Specifications:
Training Configuration:
Core:
Monitoring:
Deployment:
Hardware:
Simplest way to get started with reproducible environment:
# Clone the repository
git clone https://github.com/Vincent-PRO-AI/french-llm-from-scratch.git
cd french-llm-from-scratch
# Start dashboard
docker-compose up -d backend frontend
# Run training
docker-compose run --rm training python scripts/train_subtitles_transformer.py \
--arch-preset medium \
--tokenizer-path trained_models/tokenizers/fineweb-32k/tokenizer.json \
--pretokenized-path data_clean/mixed_tokenized.pt \
--max-steps 60000 \
--batch-size 4
# Access dashboard at http://localhost:5174
📖 Full Docker guide: See DOCKER.md for detailed instructions.
# Clone the repository
git clone https://github.com/Vincent-PRO-AI/french-llm-from-scratch.git
cd french-llm-from-scratch
# Create virtual environment
python3 -m venv .venv
source .venv/bin/activate # Linux/Mac
# or: .venv\Scripts\activate # Windows
# Install dependencies
pip install -r requirements.txt
# Download and tokenize Wikipedia FR
python scripts/prepare_wikipedia.py --max-articles 3000
python scripts/clean_wikipedia.py
# Download FineWeb-2 French subset
python scripts/download_fineweb.py --max-size-mb 50
# Tokenize corpus
python scripts/pretokenize_corpus.py \
--input-dir data_clean/wikipedia \
--tokenizer trained_models/tokenizers/fineweb-32k/tokenizer.json \
--output data_clean/wikipedia_tokenized.pt
# Launch dashboard (optional but recommended)
cd dashboard
python server.py &
cd web && npm install && npm run dev &
# Start training
python scripts/train_subtitles_transformer.py \
--arch-preset medium \
--tokenizer-path trained_models/tokenizers/fineweb-32k/tokenizer.json \
--pretokenized-path data_clean/mixed_tokenized.pt \
--max-steps 60000 \
--batch-size 4 \
--lr 0.0001 \
--checkpoint-interval 5000
# Monitor at: http://localhost:5174
# Option A: Automated pipeline (recommended)
bash scripts/run_finetune_pipeline.sh
# Option B: Manual steps
# 1. Setup HuggingFace authentication (for gated datasets like LMSYS)
python scripts/setup_hf_login.py
# 2. Download conversation datasets
python scripts/download_best_conversations.py
# 3. Tokenize new conversations
bash scripts/tokenize_conversations.sh
# 4. Combine with existing datasets
python scripts/combine_tokenized_datasets.py
# 5. Launch fine-tuning
python scripts/train_subtitles_transformer.py \
--arch-preset medium \
--resume-from trained_models/runs/french_medium_base_60k/checkpoint_step_100000.pt \
--pretokenized-path data_clean/conversations_combined_tokenized.pt \
--max-steps 115000 \
--lr 3e-6 \
--batch-size 4 \
--checkpoint-interval 2500 \
--run-name french_medium_finetune_conversations
# 6. Monitor training in real-time
bash scripts/monitor_training.sh
# Generate samples from base model
python test_model_samples.py
# Compare base vs fine-tuned models
python test_finetuned_conversation.py
# Interactive conversation testing
python test_interactive.py
french-llm-from-scratch/
├── scripts/ # Training & data pipelines
│ ├── train_subtitles_transformer.py # Main training script (torch.compile, FusedAdam)
│ ├── download_massive_conversations.py # Multi-source downloader (UltraChat, OASST2, Dolly)
│ ├── combine_all_datasets.py # Merge tokenized datasets → 197M tokens
│ ├── tokenize_massive_conversations.sh # Chunked tokenization (handles 334MB+ files)
│ ├── monitor_mega_training.sh # Real-time training monitor with ETA
│ ├── publish_to_huggingface.py # HuggingFace Hub publication script
│ ├── save_mistral_tokenizer.py # Save Mistral-7B tokenizer locally
│ └── ...
├── tests/ # Test scripts
│ ├── test_180k_vs_115k.py # Compare checkpoints quantitatively
│ ├── test_180k_tokenizer.py # Generation with proper tokenizer
│ ├── test_chat_model.py # Interactive chat testing
│ └── test_model_samples.py # Sample generation
├── visualizations/ # Publication-ready graphs (4 PNG)
│ ├── training_loss_evolution_60k_200k.png # Full training curve
│ ├── checkpoint_comparison.png # Bar chart comparison
│ ├── relative_improvement.png # % gains visualization
│ └── training_timeline.png # Temporal training view
├── dashboard/
│ ├── server.py # Flask API (metrics, chat, runs)
│ └── web/ # React + Vite frontend
├── training_configs/
│ ├── finetune_conversations.json # Fine-tuning config
│ └── continue_base_100k.json # Continue base training
├── data_clean/ # Tokenized datasets (gitignored)
│ └── conversations_mega_tokenized.pt # 197M tokens, 1.5GB
├── trained_models/ # Checkpoints & runs (gitignored)
│ └── runs/
│ ├── french_medium_mega_finetune/ # 180k checkpoint
│ ├── french_medium_mega_finetune_extended/ # 195k checkpoint
│ └── french_medium_mega_finetune_200k/ # 200k checkpoint (final)
└── finetune_conversations.py # Legacy fine-tuning script
1. Periodic Checkpoints
--checkpoint-interval)2. Learning Rate Scheduling
3. Web Dashboard
4. Pre-tokenization
.pt files5. Automated Data Pipeline
6. Real-time Training Monitoring
Local (More Time):
Hardware Upgrade:
Cloud Scaling:
Standard_NC24ads_A100_v4 (1×A100 80GB)p4d.24xlarge (8×A100 40GB), p5.2xlarge (1×H100 80GB)g5.2xlarge (A10G 24GB)If you use this code or methodology, please cite:
@misc{french-llm-from-scratch-2025,
author = {Vincent Ayari},
title = {French LLM Training from Scratch},
year = {2025},
publisher = {GitHub},
url = {https://github.com/Vincent-PRO-AI/french-llm-from-scratch}
}
MIT License - See LICENSE for details.
Full Docker support for easy deployment and reproducibility!
Quick Start:
docker-compose up -d backend frontend
docker-compose run --rm training python scripts/train_subtitles_transformer.py [options]
See DOCKER.md for:
Ce projet démontre le pipeline complet pour entraîner un modèle GPT de taille moyenne pour le français depuis zéro, fonctionnant sur une configuration multi-GPU (RTX 4090 + RTX 5080).
Réalisations clés :
Entraînement Multi-GPU DDP (200k → 250k steps)
Fine-tuning conversationnel (100k → 115k steps)
Spécifications du modèle :
Configuration d'entraînement :
Cœur :
Monitoring :
Déploiement :
Hardware :
Moyen le plus simple avec environnement reproductible :
# Cloner le dépôt
git clone https://github.com/Vincent-PRO-AI/french-llm-from-scratch.git
cd french-llm-from-scratch
# Démarrer le dashboard
docker-compose up -d backend frontend
# Lancer l'entraînement
docker-compose run --rm training python scripts/train_subtitles_transformer.py \
--arch-preset medium \
--tokenizer-path trained_models/tokenizers/fineweb-32k/tokenizer.json \
--pretokenized-path data_clean/mixed_tokenized.pt \
--max-steps 60000 \
--batch-size 4
# Accéder au dashboard : http://localhost:5174
📖 Guide Docker complet : Voir DOCKER.md pour les instructions détaillées.
# Cloner le dépôt
git clone https://github.com/Vincent-PRO-AI/french-llm-from-scratch.git
cd french-llm-from-scratch
# Créer l'environnement virtuel
python3 -m venv .venv
source .venv/bin/activate # Linux/Mac
# ou : .venv\Scripts\activate # Windows
# Installer les dépendances
pip install -r requirements.txt
# Télécharger et tokeniser Wikipedia FR
python scripts/prepare_wikipedia.py --max-articles 3000
python scripts/clean_wikipedia.py
# Télécharger le sous-ensemble français FineWeb-2
python scripts/download_fineweb.py --max-size-mb 50
# Tokeniser le corpus
python scripts/pretokenize_corpus.py \
--input-dir data_clean/wikipedia \
--tokenizer trained_models/tokenizers/fineweb-32k/tokenizer.json \
--output data_clean/wikipedia_tokenized.pt
# Lancer le dashboard (optionnel mais recommandé)
cd dashboard
python server.py &
cd web && npm install && npm run dev &
# Démarrer l'entraînement
python scripts/train_subtitles_transformer.py \
--arch-preset medium \
--tokenizer-path trained_models/tokenizers/fineweb-32k/tokenizer.json \
--pretokenized-path data_clean/mixed_tokenized.pt \
--max-steps 60000 \
--batch-size 4 \
--lr 0.0001 \
--checkpoint-interval 5000
# Monitorer sur : http://localhost:5174
# Télécharger le dataset de conversations
python scripts/download_conversations.py --max-conversations 5000
# Tokeniser les conversations
python scripts/pretokenize_corpus.py \
--input-dir data_clean/conversations \
--tokenizer trained_models/tokenizers/fineweb-32k/tokenizer.json \
--output data_clean/conversations_tokenized.pt
# Fine-tuner depuis le checkpoint
python finetune_conversations.py
# Générer des échantillons
python test_model_samples.py
# Tester les capacités conversationnelles
python test_chat_model.py
french-llm-from-scratch/
├── scripts/
│ ├── train_subtitles_transformer.py # Script d'entraînement principal
│ ├── download_conversations.py # Téléchargeur OpenAssistant
│ ├── pretokenize_corpus.py # Tokenisation du corpus
│ └── ...
├── dashboard/
│ ├── server.py # Backend Flask
│ └── web/ # Frontend React
├── data_clean/ # Datasets tokenisés (gitignored)
├── trained_models/ # Checkpoints & runs (gitignored)
├── finetune_conversations.py # Script de fine-tuning
├── test_chat_model.py # Tests conversationnels
└── test_model_samples.py # Génération d'échantillons
1. Checkpoints périodiques
--checkpoint-interval)2. Scheduling du learning rate
3. Dashboard web
4. Pré-tokenisation
.pt pré-tokenisésLocal (plus de temps) :
Upgrade matériel :
Scaling cloud :
Standard_NC24ads_A100_v4 (1×A100 80GB)p4d.24xlarge (8×A100 40GB), p5.2xlarge (1×H100 80GB)g5.2xlarge (A10G 24GB)Si vous utilisez ce code ou cette méthodologie, veuillez citer :
@misc{french-llm-from-scratch-2025,
author = {Vincent Ayari},
title = {French LLM Training from Scratch},
year = {2025},
publisher = {GitHub},
url = {https://github.com/Vincent-PRO-AI/french-llm-from-scratch}
}
Licence MIT - Voir LICENSE pour les détails.
Support Docker complet pour un déploiement facile et reproductible !
Démarrage rapide :
docker-compose up -d backend frontend
docker-compose run --rm training python scripts/train_subtitles_transformer.py [options]
Voir DOCKER.md pour :
⭐ Si ce projet vous est utile, n'hésitez pas à lui donner une étoile !
6 commits
1 commits
Python
84.3%
Shell
9.1%
JavaScript
5.2%
Training a French GPT model from scratch on local hardware with full monitoring, periodic checkpoints, and conversational fine-tuning.
🔗 Hugging Face Model: vincent-pro-ai/french-llm-from-scratch
This project demonstrates the complete pipeline for training a medium-sized GPT model for French language from scratch, running entirely on consumer hardware (RTX 5080, 16GB VRAM).
Key Achievements:
Complete Training Pipeline (60k → 250k steps)
| Checkpoint | Steps | Val Loss | Improvement | Details |
|---|---|---|---|---|
| Baseline | 60k | 7.13 | - | Initial training |
| First milestone | 115k | 4.88 | -31.6% | +15M conversation tokens |
| Scaled dataset | 180k | 4.89 | +0.2% | 197M tokens corpus |
| Final base model | 200k | 4.79 | -2% | High-quality base |
| DDP Validation | 250.5k | 3.99 | -16% | Multi-GPU stable training |
Hardware Setup:
torchrunExpanding beyond training from scratch, we've implemented a professional fine-tuning pipeline for Mistral-7B-v0.3 using QLoRA on our Multi-GPU infrastructure.
Highlights:
bfloat16 and optimized learning rate (5e-5) to avoid loss divergence.Why this matters: This demonstrates the ability to take state-of-the-art weights and customize them for French specificities on local hardware, while ensuring the result is immediately deployable.
Dataset Evolution:
Training Metrics:
Qualitative Improvements:
Model Specifications:
Training Configuration:
Core:
Monitoring:
Deployment:
Hardware:
Simplest way to get started with reproducible environment:
# Clone the repository
git clone https://github.com/Vincent-PRO-AI/french-llm-from-scratch.git
cd french-llm-from-scratch
# Start dashboard
docker-compose up -d backend frontend
# Run training
docker-compose run --rm training python scripts/train_subtitles_transformer.py \
--arch-preset medium \
--tokenizer-path trained_models/tokenizers/fineweb-32k/tokenizer.json \
--pretokenized-path data_clean/mixed_tokenized.pt \
--max-steps 60000 \
--batch-size 4
# Access dashboard at http://localhost:5174
📖 Full Docker guide: See DOCKER.md for detailed instructions.
# Clone the repository
git clone https://github.com/Vincent-PRO-AI/french-llm-from-scratch.git
cd french-llm-from-scratch
# Create virtual environment
python3 -m venv .venv
source .venv/bin/activate # Linux/Mac
# or: .venv\Scripts\activate # Windows
# Install dependencies
pip install -r requirements.txt
# Download and tokenize Wikipedia FR
python scripts/prepare_wikipedia.py --max-articles 3000
python scripts/clean_wikipedia.py
# Download FineWeb-2 French subset
python scripts/download_fineweb.py --max-size-mb 50
# Tokenize corpus
python scripts/pretokenize_corpus.py \
--input-dir data_clean/wikipedia \
--tokenizer trained_models/tokenizers/fineweb-32k/tokenizer.json \
--output data_clean/wikipedia_tokenized.pt
# Launch dashboard (optional but recommended)
cd dashboard
python server.py &
cd web && npm install && npm run dev &
# Start training
python scripts/train_subtitles_transformer.py \
--arch-preset medium \
--tokenizer-path trained_models/tokenizers/fineweb-32k/tokenizer.json \
--pretokenized-path data_clean/mixed_tokenized.pt \
--max-steps 60000 \
--batch-size 4 \
--lr 0.0001 \
--checkpoint-interval 5000
# Monitor at: http://localhost:5174
# Option A: Automated pipeline (recommended)
bash scripts/run_finetune_pipeline.sh
# Option B: Manual steps
# 1. Setup HuggingFace authentication (for gated datasets like LMSYS)
python scripts/setup_hf_login.py
# 2. Download conversation datasets
python scripts/download_best_conversations.py
# 3. Tokenize new conversations
bash scripts/tokenize_conversations.sh
# 4. Combine with existing datasets
python scripts/combine_tokenized_datasets.py
# 5. Launch fine-tuning
python scripts/train_subtitles_transformer.py \
--arch-preset medium \
--resume-from trained_models/runs/french_medium_base_60k/checkpoint_step_100000.pt \
--pretokenized-path data_clean/conversations_combined_tokenized.pt \
--max-steps 115000 \
--lr 3e-6 \
--batch-size 4 \
--checkpoint-interval 2500 \
--run-name french_medium_finetune_conversations
# 6. Monitor training in real-time
bash scripts/monitor_training.sh
# Generate samples from base model
python test_model_samples.py
# Compare base vs fine-tuned models
python test_finetuned_conversation.py
# Interactive conversation testing
python test_interactive.py
french-llm-from-scratch/
├── scripts/ # Training & data pipelines
│ ├── train_subtitles_transformer.py # Main training script (torch.compile, FusedAdam)
│ ├── download_massive_conversations.py # Multi-source downloader (UltraChat, OASST2, Dolly)
│ ├── combine_all_datasets.py # Merge tokenized datasets → 197M tokens
│ ├── tokenize_massive_conversations.sh # Chunked tokenization (handles 334MB+ files)
│ ├── monitor_mega_training.sh # Real-time training monitor with ETA
│ ├── publish_to_huggingface.py # HuggingFace Hub publication script
│ ├── save_mistral_tokenizer.py # Save Mistral-7B tokenizer locally
│ └── ...
├── tests/ # Test scripts
│ ├── test_180k_vs_115k.py # Compare checkpoints quantitatively
│ ├── test_180k_tokenizer.py # Generation with proper tokenizer
│ ├── test_chat_model.py # Interactive chat testing
│ └── test_model_samples.py # Sample generation
├── visualizations/ # Publication-ready graphs (4 PNG)
│ ├── training_loss_evolution_60k_200k.png # Full training curve
│ ├── checkpoint_comparison.png # Bar chart comparison
│ ├── relative_improvement.png # % gains visualization
│ └── training_timeline.png # Temporal training view
├── dashboard/
│ ├── server.py # Flask API (metrics, chat, runs)
│ └── web/ # React + Vite frontend
├── training_configs/
│ ├── finetune_conversations.json # Fine-tuning config
│ └── continue_base_100k.json # Continue base training
├── data_clean/ # Tokenized datasets (gitignored)
│ └── conversations_mega_tokenized.pt # 197M tokens, 1.5GB
├── trained_models/ # Checkpoints & runs (gitignored)
│ └── runs/
│ ├── french_medium_mega_finetune/ # 180k checkpoint
│ ├── french_medium_mega_finetune_extended/ # 195k checkpoint
│ └── french_medium_mega_finetune_200k/ # 200k checkpoint (final)
└── finetune_conversations.py # Legacy fine-tuning script
1. Periodic Checkpoints
--checkpoint-interval)2. Learning Rate Scheduling
3. Web Dashboard
4. Pre-tokenization
.pt files5. Automated Data Pipeline
6. Real-time Training Monitoring
Local (More Time):
Hardware Upgrade:
Cloud Scaling:
Standard_NC24ads_A100_v4 (1×A100 80GB)p4d.24xlarge (8×A100 40GB), p5.2xlarge (1×H100 80GB)g5.2xlarge (A10G 24GB)If you use this code or methodology, please cite:
@misc{french-llm-from-scratch-2025,
author = {Vincent Ayari},
title = {French LLM Training from Scratch},
year = {2025},
publisher = {GitHub},
url = {https://github.com/Vincent-PRO-AI/french-llm-from-scratch}
}
MIT License - See LICENSE for details.
Full Docker support for easy deployment and reproducibility!
Quick Start:
docker-compose up -d backend frontend
docker-compose run --rm training python scripts/train_subtitles_transformer.py [options]
See DOCKER.md for:
Ce projet démontre le pipeline complet pour entraîner un modèle GPT de taille moyenne pour le français depuis zéro, fonctionnant sur une configuration multi-GPU (RTX 4090 + RTX 5080).
Réalisations clés :
Entraînement Multi-GPU DDP (200k → 250k steps)
Fine-tuning conversationnel (100k → 115k steps)
Spécifications du modèle :
Configuration d'entraînement :
Cœur :
Monitoring :
Déploiement :
Hardware :
Moyen le plus simple avec environnement reproductible :
# Cloner le dépôt
git clone https://github.com/Vincent-PRO-AI/french-llm-from-scratch.git
cd french-llm-from-scratch
# Démarrer le dashboard
docker-compose up -d backend frontend
# Lancer l'entraînement
docker-compose run --rm training python scripts/train_subtitles_transformer.py \
--arch-preset medium \
--tokenizer-path trained_models/tokenizers/fineweb-32k/tokenizer.json \
--pretokenized-path data_clean/mixed_tokenized.pt \
--max-steps 60000 \
--batch-size 4
# Accéder au dashboard : http://localhost:5174
📖 Guide Docker complet : Voir DOCKER.md pour les instructions détaillées.
# Cloner le dépôt
git clone https://github.com/Vincent-PRO-AI/french-llm-from-scratch.git
cd french-llm-from-scratch
# Créer l'environnement virtuel
python3 -m venv .venv
source .venv/bin/activate # Linux/Mac
# ou : .venv\Scripts\activate # Windows
# Installer les dépendances
pip install -r requirements.txt
# Télécharger et tokeniser Wikipedia FR
python scripts/prepare_wikipedia.py --max-articles 3000
python scripts/clean_wikipedia.py
# Télécharger le sous-ensemble français FineWeb-2
python scripts/download_fineweb.py --max-size-mb 50
# Tokeniser le corpus
python scripts/pretokenize_corpus.py \
--input-dir data_clean/wikipedia \
--tokenizer trained_models/tokenizers/fineweb-32k/tokenizer.json \
--output data_clean/wikipedia_tokenized.pt
# Lancer le dashboard (optionnel mais recommandé)
cd dashboard
python server.py &
cd web && npm install && npm run dev &
# Démarrer l'entraînement
python scripts/train_subtitles_transformer.py \
--arch-preset medium \
--tokenizer-path trained_models/tokenizers/fineweb-32k/tokenizer.json \
--pretokenized-path data_clean/mixed_tokenized.pt \
--max-steps 60000 \
--batch-size 4 \
--lr 0.0001 \
--checkpoint-interval 5000
# Monitorer sur : http://localhost:5174
# Télécharger le dataset de conversations
python scripts/download_conversations.py --max-conversations 5000
# Tokeniser les conversations
python scripts/pretokenize_corpus.py \
--input-dir data_clean/conversations \
--tokenizer trained_models/tokenizers/fineweb-32k/tokenizer.json \
--output data_clean/conversations_tokenized.pt
# Fine-tuner depuis le checkpoint
python finetune_conversations.py
# Générer des échantillons
python test_model_samples.py
# Tester les capacités conversationnelles
python test_chat_model.py
french-llm-from-scratch/
├── scripts/
│ ├── train_subtitles_transformer.py # Script d'entraînement principal
│ ├── download_conversations.py # Téléchargeur OpenAssistant
│ ├── pretokenize_corpus.py # Tokenisation du corpus
│ └── ...
├── dashboard/
│ ├── server.py # Backend Flask
│ └── web/ # Frontend React
├── data_clean/ # Datasets tokenisés (gitignored)
├── trained_models/ # Checkpoints & runs (gitignored)
├── finetune_conversations.py # Script de fine-tuning
├── test_chat_model.py # Tests conversationnels
└── test_model_samples.py # Génération d'échantillons
1. Checkpoints périodiques
--checkpoint-interval)2. Scheduling du learning rate
3. Dashboard web
4. Pré-tokenisation
.pt pré-tokenisésLocal (plus de temps) :
Upgrade matériel :
Scaling cloud :
Standard_NC24ads_A100_v4 (1×A100 80GB)p4d.24xlarge (8×A100 40GB), p5.2xlarge (1×H100 80GB)g5.2xlarge (A10G 24GB)Si vous utilisez ce code ou cette méthodologie, veuillez citer :
@misc{french-llm-from-scratch-2025,
author = {Vincent Ayari},
title = {French LLM Training from Scratch},
year = {2025},
publisher = {GitHub},
url = {https://github.com/Vincent-PRO-AI/french-llm-from-scratch}
}
Licence MIT - Voir LICENSE pour les détails.
Support Docker complet pour un déploiement facile et reproductible !
Démarrage rapide :
docker-compose up -d backend frontend
docker-compose run --rm training python scripts/train_subtitles_transformer.py [options]
Voir DOCKER.md pour :
⭐ Si ce projet vous est utile, n'hésitez pas à lui donner une étoile !
6 commits
1 commits
Python
84.3%
Shell
9.1%
JavaScript
5.2%