Compare commits

..

3 Commits

Author SHA1 Message Date
Alex
182e6e7a98 finnetunning 2026-01-12 00:14:26 +01:00
Alex
50f5bef7f1 finetunning 2026-01-08 23:46:32 +01:00
Alex
9c3ac3f977 retraduction de certains passages 2026-01-07 10:12:53 +01:00
10 changed files with 67788 additions and 92 deletions

File diff suppressed because it is too large Load Diff

152
Finetunning/finetunning.py Normal file
View File

@ -0,0 +1,152 @@
import torch
from datasets import load_dataset
from transformers import (
AutoTokenizer,
AutoModelForCausalLM,
TrainingArguments,
)
from peft import (
LoraConfig,
get_peft_model,
prepare_model_for_kbit_training,
)
from trl import SFTTrainer
import os
os.environ["TORCHDYNAMO_DISABLE"] = "1"
# ----------------------------
# Model configuration
# ----------------------------
MODEL_NAME = "Qwen/Qwen2.5-14B-Instruct"
print("=== Starting fine-tuning script ===")
print(f"{80 * '_'}\n[1/7] Loading tokenizer...")
tokenizer = AutoTokenizer.from_pretrained(
MODEL_NAME,
trust_remote_code=True
)
# Ensure padding token is defined
tokenizer.pad_token = tokenizer.eos_token
tokenizer.model_max_length = 1024
print("Tokenizer loaded and configured.")
print(f"{80 * '_'}\n[2/7] Loading model in 4-bit mode (QLoRA)...")
model = AutoModelForCausalLM.from_pretrained(
MODEL_NAME,
load_in_4bit=True,
device_map="auto",
torch_dtype=torch.float16, # OK for weights
trust_remote_code=True,
)
print("Model loaded.")
print(f"{80 * '_'}\n[3/7] Preparing model for k-bit training...")
model = prepare_model_for_kbit_training(model)
print("Model prepared for k-bit training.")
# ----------------------------
# LoRA configuration
# ----------------------------
print(f"{80 * '_'}\n[4/7] Configuring LoRA adapters...")
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",
"gate_proj",
"up_proj",
"down_proj",
],
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
print("LoRA adapters attached to the model.")
# ----------------------------
# Dataset loading
# ----------------------------
print(f"{80 * '_'}\n[5/7] Loading dataset from JSON file...")
dataset = load_dataset(
"json",
data_files="traductions.json"
)
print(f"Dataset loaded with {len(dataset['train'])} samples.")
print("Formatting dataset for Ukrainian → French translation...")
def format_prompt(example):
prompt = (
"Translate the following Ukrainian text into French.\n\n"
f"Ukrainian: {example['text']}\n"
f"French: {example['translation']}"
)
return {"text": prompt}
dataset = dataset.map(format_prompt, remove_columns=dataset["train"].column_names)
print("Dataset formatting completed.")
# ----------------------------
# Training arguments
# ----------------------------
print(f"{80 * '_'}\n[6/7] Initializing training arguments...")
training_args = TrainingArguments(
output_dir="./qwen-uk-fr-lora",
per_device_train_batch_size=1,
gradient_accumulation_steps=8,
learning_rate=2e-4,
num_train_epochs=3,
fp16=False,
bf16=False,
logging_steps=10,
save_steps=500,
save_total_limit=2,
# Use 32-bit optimizer
optim="paged_adamw_32bit",
report_to="none",
)
print("Training arguments ready.")
# ----------------------------
# Trainer
# ----------------------------
print("Initializing SFTTrainer...")
trainer = SFTTrainer(
model=model,
train_dataset=dataset["train"],
processing_class=tokenizer,
args=training_args,
)
print("Trainer initialized.")
# ----------------------------
# Train
# ----------------------------
print(f"{80 * '_'}\n[7/7] Starting training...")
trainer.train()
print("Training completed successfully.")
# ----------------------------
# Save LoRA adapter
# ----------------------------
print("Saving LoRA adapter and tokenizer...")
trainer.model.save_pretrained("./qwen-uk-fr-lora")
tokenizer.save_pretrained("./qwen-uk-fr-lora")
print("=== Fine-tuning finished ===")
print("LoRA adapter saved in ./qwen-uk-fr-lora")

33778
Finetunning/traductions.json Normal file

File diff suppressed because it is too large Load Diff

35
Finetunning/tsv2json.py Normal file
View File

@ -0,0 +1,35 @@
import json
from collections import defaultdict
# Chemin vers ton fichier d'entrée et de sortie
input_file = "Paires de phrases en ukrainien-français - 2026-01-06.tsv" # Remplace par ton chemin
output_file = "paires.json" # Fichier de sortie
# Dictionnaire pour stocker les paires uniques (clé = phrase ukrainienne, valeur = liste de traductions)
unique_pairs = defaultdict(list)
# Lire le fichier d'entrée
with open(input_file, "r", encoding="utf-8") as f:
for line in f:
# Diviser la ligne en colonnes (séparateur = tabulation)
parts = line.strip().split("\t")
if len(parts) >= 3:
uk_text = parts[1] # Texte ukrainien
fr_text = parts[3] # Traduction française
# Ajouter la paire au dictionnaire (évite les doublons)
if fr_text not in unique_pairs[uk_text]:
unique_pairs[uk_text].append(fr_text)
# Écrire le fichier JSONL de sortie
with open(output_file, "w", encoding="utf-8") as f_out:
for uk_text, fr_translations in unique_pairs.items():
# Prendre la première traduction (ou toutes si tu préfères)
for fr_text in fr_translations:
# Créer une entrée JSONL
entry = {
"text": uk_text,
"translation": fr_text
}
f_out.write(json.dumps(entry, ensure_ascii=False) + "\n")
print(f"Fichier JSONL généré : {output_file}")

View File

@ -9,29 +9,6 @@ Ce projet permet de traduire un document PDF page par page en utilisant un modè
- **Python** (version 3.8 ou supérieure) - **Python** (version 3.8 ou supérieure)
- **Ollama** installé et en cours d'exécution sur ta machine (en mode "serveur") - **Ollama** installé et en cours d'exécution sur ta machine (en mode "serveur")
- Un **document PDF** à traduire - Un **document PDF** à traduire
- Un modèle LLM spécialisé dans la traduction avec un context long.
---
## Création d'un modèle LLM de traduction avec Ollama
En partant du LLM [zongwei/gemma3-translator:4b](https://ollama.com/zongwei/gemma3-translator), nous allons créer un modèle optimisé pour la traduction avec Ollama.
Pour info : Inutile de le downloader, il le serra automatiquement au lancement de la commande.
```shell
FROM zongwei/gemma3-translator:4b
PARAMETER temperature 0.1
PARAMETER num_ctx 131072
SYSTEM """
You are a professional translator specialising in translating Ukrainian text into English.
Translate accurately and naturally, respecting the original intonation used by the author of the text.
You must not interpret the author's thoughts or reflections.
Do not add any text before or after the text provided.
"""
```
Il faut ensuite compiler le modèle avec la commande :
```
ollama create traductionUkrainienVersFrancais -f .\Modelfile
```
## Installation ## Installation
@ -48,7 +25,12 @@ ollama create traductionUkrainienVersFrancais -f .\Modelfile
pip install -r requirements.txt pip install -r requirements.txt
``` ```
3. **Placer votre fichier PDF** dans le répertoire du projet avec le nom configuré dans `main.py` (par défaut : `TaniaBorecMemoir(Ukr).pdf`) Puis faire :
```bash
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
```
3. **Placer votre fichier PDF** dans le répertoire `Traduction` du projet avec le nom configuré dans `main.py` (par défaut : `TaniaBorecMemoir(Ukr).pdf`)
--- ---
@ -61,27 +43,6 @@ ollama create traductionUkrainienVersFrancais -f .\Modelfile
ollama serve ollama serve
``` ```
2. **Vérifier que le modèle de traduction est disponible**
```bash
ollama list
```
Vous devez voir `traductionUkrainienVersFrancais` dans la liste.
Si ce n'est pas le cas, vous devez le générer comme décrit plus haut (paragraphe "Création d'un modèle LLM de traduction avec Ollama")
3. **Placer votre PDF** dans le même répertoire que le script `main.py`
### Paramétrage du script
`PDF_PATH`= "TaniaBorecMemoir(Ukr).pdf" <- Le nom du fichier pdf à traduire.
`OLLAMA_MODEL` = "mitmul/plamo-2-translate:latest" <- Le nom
`OLLAMA_URL` = "http://localhost:11434/api/generate" <- URL par défaut d'Ollama
`TARGET_LANGUAGE` = "français" <- Langue cible (ex: "français", "anglais", "allemand", "espagnol", etc.)
`SYSTEM_PROMPT` = """You are a professional translator specialising in Ukrainian text translation.
Translate accurately and naturally, respecting the original intonation used by the author of the text.
You must not interpret the author's thoughts or reflections.
Do not add any text before or after the text provided.
Preserve the layout and structure of the original text."""
### Exécution ### Exécution
1. **Lancer le script de traduction** 1. **Lancer le script de traduction**
@ -119,19 +80,3 @@ Vous pouvez modifier les paramètres suivants dans `main.py` :
- `OUTPUT_PDF_PATH` : Chemin et nom du fichier PDF de sortie (généré autoamtiquement) - `OUTPUT_PDF_PATH` : Chemin et nom du fichier PDF de sortie (généré autoamtiquement)
--- ---
## Dépannage
### Erreur : "Connexion refusée à Ollama"
- Vérifiez que Ollama est en cours d'exécution avec `ollama serve`
- Vérifiez que l'URL est correcte (par défaut : `http://localhost:11434`)
### Erreur : "Modèle non trouvé"
- Exécutez : `ollama create traductionUkrainienVersFrancais -f .\Modelfile`
### Le texte n'est pas bien séparé en paragraphes
- Le script détecte automatiquement les doubles sauts de ligne
- Si absent, il divise par phrases et regroupe en chunks de 1500 caractères
---

View File

@ -7,10 +7,10 @@ Tu es un traducteur spécialisé dans les mémoires ukrainiennes des années 191
- Utilise le glossaire fourni pour les noms de lieux et termes historiques. - Utilise le glossaire fourni pour les noms de lieux et termes historiques.
- Garde le style narratif et les tournures orales de l'auteur. - Garde le style narratif et les tournures orales de l'auteur.
Règles strictes : Règles strictes :
1. **Conserve tous les noms de lieux** dans leur forme originale (ex. : Львів → Lviv, mais ajoute une note si nécessaire : "[Lemberg en 1910]"). 1. **Conserve tous les noms de lieux** dans leur forme originale (ex. : Львів → Lviv, mais ajoute une note si nécessaire entre [ ]).
2. **Respecte le style narratif** : garde les tournures orales et les expressions propres à lauteur. 2. **Respecte le style narratif** : garde les tournures orales et les expressions propres à lauteur.
3. **Pour les termes historiques** (ex. : "powiat"), utilise le terme français standard ou ajoute une note explicative. 3. **Pour les termes historiques** (ex. : "powiat"), utilise le terme français standard et ajoute une note explicative.
4. **Ne traduis pas** les mots en russe/allemand/polonais intégrés au texte (ex. : citations, noms officiels). 4. **Conserve les citations** russe/allemand/polonais intégrés au texte (mais ajoute une note de fin de paragraphe entre [ ] en la traduisant et en précisant la langue d'origine.
5. **Structure** : Garde les sauts de ligne et la mise en page originale. 5. **Structure** : Garde les sauts de ligne et la mise en page originale.
6. **Notes du traducteur** : Ajoute entre crochets [ ] les explications contextuelles (ex. : "[Note : ville alors sous domination autrichienne]"). 6. **Notes du traducteur** : Ajoute entre crochets [ ] les explications contextuelles si un contexte historique existe (ex. : "[Note : le context]").
""" """

View File

@ -12,14 +12,14 @@ import os
# Configuration # Configuration
DEBUG = True DEBUG = True
PDF_PATH = "TaniaBorecMemoir(Ukr).pdf" PDF_PATH = "Traduction\TaniaBorecMemoir(Ukr).pdf"
OLLAMA_MODEL = "traductionUkrainienVersFrancais:latest" OLLAMA_MODEL = "traductionUkrainienVersFrancais:latest"
OLLAMA_URL = "http://localhost:11434/api/generate" OLLAMA_URL = "http://localhost:11434/api/generate"
TARGET_LANGUAGE = "français" TARGET_LANGUAGE = "français"
CHECKPOINT_FILE = "checkpoint.json" CHECKPOINT_FILE = "Traduction\checkpoint.json"
TEMP_OUTPUT_TXT = "output_temp.txt" TEMP_OUTPUT_TXT = "Traduction\output_temp.txt"
FINAL_OUTPUT_PDF = PDF_PATH.replace(".pdf",f"({TARGET_LANGUAGE.upper()[:2]})_V6.pdf") FINAL_OUTPUT_PDF = PDF_PATH.replace(".pdf",f"({TARGET_LANGUAGE.upper()[:2]})_V8.pdf")
FINAL_OUTPUT_TXT = PDF_PATH.replace(".pdf",f"({TARGET_LANGUAGE.upper()[:2]})_V6.txt") FINAL_OUTPUT_TXT = PDF_PATH.replace(".pdf",f"({TARGET_LANGUAGE.upper()[:2]})_V8.txt")
DEBUG = True DEBUG = True

View File

@ -1,8 +1,16 @@
certifi==2026.1.4 certifi
charset-normalizer==3.4.4 charset-normalizer
idna==3.11 idna
pillow==12.1.0 pillow
PyPDF2==3.0.1 PyPDF2
reportlab==4.4.7 reportlab
requests==2.32.5 requests
urllib3==2.6.2 urllib3
torch
transformers
datasets
peft
bitsandbytes
accelerate
trl

View File

@ -11,18 +11,18 @@ REM Chemin vers l'environnement virtuel Python (relatif au répertoire courant)
set VENV_PATH=%CURRENT_DIR%\venv set VENV_PATH=%CURRENT_DIR%\venv
REM Chemin vers votre script principal (relatif au répertoire courant) REM Chemin vers votre script principal (relatif au répertoire courant)
set MAIN_SCRIPT_PATH=%CURRENT_DIR%\main.py set MAIN_SCRIPT_PATH=%CURRENT_DIR%\Traduction\main.py
REM Activer l'environnement virtuel Python REM Activer l'environnement virtuel Python
call %VENV_PATH%\Scripts\activate.bat call %VENV_PATH%\Scripts\activate.bat
REM Lancer la compilation du modèle LLM pour Ollama REM Lancer la compilation du modèle LLM pour Ollama
ollama create traductionUkrainienVersFrancais -f .\Modelfile ollama create traductionUkrainienVersFrancais -f .\Traduction\Modelfile
:: 1. Vérifie si le processus ollama.exe est en cours d'exécution :: 1. Vérifie si le processus ollama.exe est en cours d'exécution
tasklist | find "ollama.exe" >nul tasklist | find "ollama.exe" >nul
if %ERRORLEVEL% equ 0 ( if %ERRORLEVEL% equ 0 (
echo [OK] Le processus Ollama est en cours d'execution. echo [OK] Le processus Ollama est bien en cours d'execution.
) else ( ) else (
echo [ERREUR] Ollama n'est pas lancé. echo [ERREUR] Ollama n'est pas lancé.
pause pause