Compare commits

..

32 Commits

Author SHA1 Message Date
Alex
dc66ac9520 utilisation du LLM translategemma:12b 2026-02-11 17:29:59 +01:00
Alex
8b45028101 Suppression des fichiers PDF et TXT dans le répertoire Traduction 2026-02-11 17:11:46 +01:00
Alex
ef4515adcc remove pdf files 2026-02-11 17:07:57 +01:00
Alex
7aea840821 optimisation pour traduction plus fuilde 2026-02-11 17:06:07 +01:00
Alex
71e595a966 fine tunning 2026-02-11 17:02:29 +01:00
Alex
d5313fb143 version fonctionnelle 2026-01-15 19:11:55 +01:00
Alex
51e114b1ee meilleur gestion de la reprise du trainning 2026-01-15 17:03:18 +01:00
Alex
fa3ad61dd7 utilisation du GPU 2026-01-15 16:59:25 +01:00
Alex
70e4932cd0 optimisation 2026-01-14 23:54:12 +01:00
Alex
aee2716a41 nettoyage dataset 2026-01-14 23:47:39 +01:00
Alex
bf7949d8c3 conversion en LLM 2026-01-14 18:41:27 +01:00
Alex
a4296d012e méthode à suivre 2026-01-14 18:12:25 +01:00
Alex
c5d372e98d ne pas versioner le lora 2026-01-14 18:00:42 +01:00
Alex
8d2e5ac021 optimisation de la relance 2026-01-14 17:59:36 +01:00
Alex
adca297850 validation 2026-01-14 17:59:12 +01:00
Alex
83b2eccd07 validation du modele 2026-01-14 12:12:39 +01:00
Alex
8dfb2b81e0 conversion du fichier tsv en json 2026-01-12 19:55:01 +01:00
Alex
4ed1ffa226 autorise la reprise d'unentrainement 2026-01-12 14:40:23 +01:00
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
Alex
defcc38435 meilleur traducteur 2026-01-06 20:54:24 +01:00
Alex
e9a090ad9d typo 2026-01-05 18:42:00 +01:00
Alex
156220e62b gestion des reprises de traductions 2026-01-05 18:40:52 +01:00
Alex
89c2197a42 affichage des paragraphes en mode debug 2026-01-05 17:13:40 +01:00
Alex
aa2ab7dbec la meilleur traduction 2026-01-05 12:59:46 +01:00
Alex
32b88aa9b8 lancement depuis le shel 2026-01-05 12:28:01 +01:00
Alex
dacd665535 update 2026-01-05 12:00:23 +01:00
Alex
a37b962a05 meilleur LLM de traduction 2026-01-05 12:00:03 +01:00
Alex
ca862aa9e3 Ajouts des fonctions manquantes 2026-01-05 11:59:35 +01:00
Alex
71786d27c6 reprise du programme en cas d'erreur ou plantage 2026-01-05 11:04:51 +01:00
Alex
d9fae5f658 traduction de 7 en 7 paragraphes 2026-01-05 09:48:22 +01:00
19 changed files with 77913 additions and 192 deletions

6
.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
output_temp.txt
checkpoint.json
Traduction/Modelfile
.env
Traduction/*.pdf
Traduction/*.txt

2
Finetunning/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
# Les modèles générés
qwen2.5*/

File diff suppressed because it is too large Load Diff

144
Finetunning/cleanDataSet.py Normal file
View File

@@ -0,0 +1,144 @@
import json
import unicodedata
import re
from collections import OrderedDict
# ----------------------------
# Configuration
# ----------------------------
INPUT_FILE = "paires.json"
OUTPUT_FILE = "paires_clean.json"
MIN_TOKENS = 5
MAX_TOKENS = 200
MIN_QUALITY_SCORE = 0.60
print("=== Dataset cleaning + quality scoring started ===")
# ----------------------------
# Normalization helpers
# ----------------------------
def normalize_text(text: str) -> str:
text = unicodedata.normalize("NFKC", text)
text = re.sub(r"\s+", " ", text).strip()
text = text.replace("", "'").replace("", "'").replace("", '"').replace("", '"')
return text
def token_count(text: str) -> int:
return len(text.split())
# ----------------------------
# Quality scoring
# ----------------------------
def length_ratio_score(src_len, tgt_len):
"""
Ideal ratio FR/UK ≈ 0.9 1.3
"""
ratio = tgt_len / max(src_len, 1)
if ratio < 0.5 or ratio > 2.0:
return 0.0
elif 0.75 <= ratio <= 1.5:
return 1.0
else:
return max(0.0, 1.0 - abs(ratio - 1.1))
def lexical_density_score(text):
"""
Penalize very repetitive or trivial translations
"""
tokens = text.split()
if not tokens:
return 0.0
unique_ratio = len(set(tokens)) / len(tokens)
return min(1.0, unique_ratio * 1.5)
def quality_score(src, tgt):
src_len = token_count(src)
tgt_len = token_count(tgt)
l_score = length_ratio_score(src_len, tgt_len)
d_score = lexical_density_score(tgt)
return 0.7 * l_score + 0.3 * d_score
# ----------------------------
# Load + clean + score
# ----------------------------
unique_sources = OrderedDict()
stats = {
"total": 0,
"removed_length": 0,
"removed_duplicates": 0,
"removed_quality": 0,
}
with open(INPUT_FILE, "r", encoding="utf-8") as f:
for line in f:
stats["total"] += 1
item = json.loads(line)
src = normalize_text(item["text"])
tgt = normalize_text(item["translation"])
src_len = token_count(src)
tgt_len = token_count(tgt)
# Length filtering
if not (MIN_TOKENS <= src_len <= MAX_TOKENS):
stats["removed_length"] += 1
continue
if not (MIN_TOKENS <= tgt_len <= MAX_TOKENS):
stats["removed_length"] += 1
continue
# Deduplication
if src in unique_sources:
stats["removed_duplicates"] += 1
continue
# Quality score
q_score = quality_score(src, tgt)
if q_score < MIN_QUALITY_SCORE:
stats["removed_quality"] += 1
continue
unique_sources[src] = {
"translation": tgt,
"quality_score": round(q_score, 3)
}
# ----------------------------
# Report
# ----------------------------
print(f"Total lines processed: {stats['total']}")
print(f"Removed (length): {stats['removed_length']}")
print(f"Removed (duplicates): {stats['removed_duplicates']}")
print(f"Removed (quality): {stats['removed_quality']}")
print(f"Final kept pairs: {len(unique_sources)}")
# ----------------------------
# Save cleaned dataset
# ----------------------------
with open(OUTPUT_FILE, "w", encoding="utf-8") as f:
for src, data in unique_sources.items():
json.dump(
{
"text": src,
"translation": data["translation"],
"quality_score": data["quality_score"],
},
f,
ensure_ascii=False
)
f.write("\n")
print(f"=== Cleaning completed ===")
print(f"Clean dataset saved to: {OUTPUT_FILE}")

210
Finetunning/finetunning.py Normal file
View File

@@ -0,0 +1,210 @@
import os
import torch
from datasets import load_dataset
from transformers import (
AutoTokenizer,
AutoModelForCausalLM,
TrainingArguments,
BitsAndBytesConfig,
)
from peft import (
LoraConfig,
get_peft_model,
prepare_model_for_kbit_training,
)
from trl import SFTTrainer
# ----------------------------
# Environment safety (Windows + AMP fixes)
# ----------------------------
os.environ["TORCHDYNAMO_DISABLE"] = "1"
os.environ["ACCELERATE_MIXED_PRECISION"] = "no" # ✅ disable AMP completely
os.environ["TORCH_AMP_DISABLE"] = "1" # ✅ disable GradScaler
os.environ["CUDA_VISIBLE_DEVICES"] = "0" # optional: force first GPU
# ----------------------------
# Global configuration
# ----------------------------
MODEL_NAME = "Qwen/Qwen2.5-7B-Instruct"
OUTPUT_DIR = "./qwen2.5-7b-uk-fr-lora"
DATA_FILE = "paires_clean.json"
MAX_SEQ_LENGTH = 512 # Reduce for RTX 4080 SUPER VRAM
print(f"\n=== Starting fine-tuning script for {MODEL_NAME} ===\n")
# ----------------------------
# [1/7] Tokenizer
# ----------------------------
print(f"{80 * '_'}\n[1/7] Loading tokenizer...")
tokenizer = AutoTokenizer.from_pretrained(
MODEL_NAME,
trust_remote_code=True,
)
tokenizer.pad_token = tokenizer.eos_token
tokenizer.model_max_length = MAX_SEQ_LENGTH
print("Tokenizer loaded.")
print(f"Pad token id: {tokenizer.pad_token_id}")
print(f"Max sequence length: {tokenizer.model_max_length}")
# ----------------------------
# [2/7] Load model in 4-bit (QLoRA)
# ----------------------------
print(f"{80 * '_'}\n[2/7] Loading model in 4-bit mode (QLoRA)...")
assert torch.cuda.is_available(), "CUDA GPU not detected!"
print(f"Using GPU: {torch.cuda.get_device_name(0)}")
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.float16, # fp16 internally
bnb_4bit_use_double_quant=True,
)
model = AutoModelForCausalLM.from_pretrained(
MODEL_NAME,
device_map="auto",
quantization_config=bnb_config,
low_cpu_mem_usage=True,
trust_remote_code=True,
)
# Align model tokens with tokenizer
model.config.pad_token_id = tokenizer.pad_token_id
model.config.bos_token_id = tokenizer.bos_token_id
model.config.eos_token_id = tokenizer.eos_token_id
print("Model loaded successfully in 4-bit mode on GPU.")
# ----------------------------
# [3/7] Prepare model for k-bit training
# ----------------------------
print(f"{80 * '_'}\n[3/7] Preparing model for k-bit training...")
model = prepare_model_for_kbit_training(model)
model.gradient_checkpointing_enable(gradient_checkpointing_kwargs={"use_reentrant": False})
model.config.use_cache = False # Important with gradient checkpointing + QLoRA
print("Model prepared for k-bit training.")
# ----------------------------
# [4/7] LoRA configuration
# ----------------------------
print(f"{80 * '_'}\n[4/7] Configuring LoRA adapters...")
lora_config = LoraConfig(
r=32,
lora_alpha=64,
lora_dropout=0.02,
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 successfully attached.")
# ----------------------------
# [5/7] Dataset loading & formatting
# ----------------------------
print(f"{80 * '_'}\n[5/7] Loading dataset from JSON file...")
dataset = load_dataset("json", data_files=DATA_FILE)
print(f"Dataset loaded with {len(dataset['train'])} samples.")
print("Formatting dataset for Ukrainian → French translation...")
def format_prompt(example):
return {
"text": (
"<|im_start|>user\n"
"Translate the following Ukrainian text into French.\n"
f"Ukrainian: {example['text']}\n"
"<|im_end|>\n"
"<|im_start|>assistant\n"
f"{example['translation']}"
"<|im_end|>"
)
}
dataset = dataset.map(
format_prompt,
remove_columns=dataset["train"].column_names
)
print("Dataset formatting completed.")
print("Example prompt:\n")
print(dataset["train"][0]["text"])
# ----------------------------
# [6/7] Training arguments
# ----------------------------
print(f"{80 * '_'}\n[6/7] Initializing training arguments...")
training_args = TrainingArguments(
output_dir=OUTPUT_DIR,
per_device_train_batch_size=1,
gradient_accumulation_steps=16,
learning_rate=1e-4,
num_train_epochs=2,
max_steps=1000,
fp16=False, # ⚠ disable AMP
bf16=False, # ⚠ disable BF16
optim="paged_adamw_32bit",
logging_steps=10,
save_steps=500,
save_total_limit=2,
report_to="none",
dataloader_pin_memory=False,
max_grad_norm=0.0, # avoid AMP gradient clipping
)
print("Training arguments ready.")
print(f"Output directory: {OUTPUT_DIR}")
print(f"Epochs: {training_args.num_train_epochs}")
print(f"Effective batch size: {training_args.per_device_train_batch_size * training_args.gradient_accumulation_steps}")
# ----------------------------
# [7/7] Trainer
# ----------------------------
print(f"{80 * '_'}\nInitializing SFTTrainer...")
trainer = SFTTrainer(
model=model,
train_dataset=dataset["train"],
args=training_args,
)
print("Trainer initialized.")
# ----------------------------
# Training
# ----------------------------
print(f"{80 * '_'}\nStarting training...")
checkpoint_exists = False
if os.path.exists(OUTPUT_DIR):
checkpoint_exists = any(
d.startswith("checkpoint-")
for d in os.listdir(OUTPUT_DIR)
)
if checkpoint_exists:
print("Checkpoint found → resuming training")
train_output = trainer.train(resume_from_checkpoint=True)
else:
print("No checkpoint found → starting fresh training")
train_output = trainer.train()
print("\n=== Training summary ===")
print(f"Global steps: {train_output.global_step}")
print(f"Training loss: {train_output.training_loss}")
print(f"Metrics: {train_output.metrics}")
print("Training completed successfully.")
# ----------------------------
# Save LoRA adapter and tokenizer
# ----------------------------
print(f"{80 * '_'}\nSaving LoRA adapter and tokenizer...")
trainer.model.save_pretrained(OUTPUT_DIR)
tokenizer.save_pretrained(OUTPUT_DIR)
print("\n=== Fine-tuning finished ===")
print(f"LoRA adapter saved in: {OUTPUT_DIR}")

68
Finetunning/mergeLora.py Normal file
View File

@@ -0,0 +1,68 @@
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
# ----------------------------
# Configuration
# ----------------------------
BASE_MODEL = "Qwen/Qwen2.5-7B-Instruct"
LORA_DIR = "./qwen2.5-7b-uk-fr-lora" # dossier issu du fine-tuning
OUTPUT_DIR = "./qwen2.5-7b-uk-fr-merged" # modèle fusionné final
DTYPE = torch.float16 # GGUF-friendly
DEVICE = "cpu" # merge sur CPU (stable, sûr)
print("=== LoRA merge script started ===")
# ----------------------------
# Load base model
# ----------------------------
print(f"{80 * '_'}\n[1/4] Loading base model...")
base_model = AutoModelForCausalLM.from_pretrained(
BASE_MODEL,
torch_dtype=DTYPE,
device_map=DEVICE,
trust_remote_code=True,
)
print("Base model loaded.")
# ----------------------------
# Load tokenizer
# ----------------------------
print(f"{80 * '_'}\n[2/4] Loading tokenizer...")
tokenizer = AutoTokenizer.from_pretrained(
BASE_MODEL,
trust_remote_code=True
)
tokenizer.pad_token = tokenizer.eos_token
print("Tokenizer loaded.")
# ----------------------------
# Load LoRA adapter
# ----------------------------
print(f"{80 * '_'}\n[3/4] Loading LoRA adapter...")
model = PeftModel.from_pretrained(
base_model,
LORA_DIR,
)
print("LoRA adapter loaded.")
# ----------------------------
# Merge LoRA into base model
# ----------------------------
print(f"{80 * '_'}\n[4/4] Merging LoRA into base model...")
model = model.merge_and_unload()
print("LoRA successfully merged.")
# ----------------------------
# Save merged model
# ----------------------------
print("Saving merged model...")
model.save_pretrained(
OUTPUT_DIR,
safe_serialization=True,
)
tokenizer.save_pretrained(OUTPUT_DIR)
print("=== Merge completed successfully ===")
print(f"Merged model saved in: {OUTPUT_DIR}")

33773
Finetunning/paires.json Normal file

File diff suppressed because it is too large Load Diff

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-francais-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

@@ -0,0 +1,30 @@
{"text": "Як би ти не намагався, ти не вивчиш англійську за два-три місяці.", "translation": "Quels que soient tes efforts, tu ne pourras pas apprendre langlais en deux-trois mois."}
{"text": "Поки я не подзвонив, він не прийшов.", "translation": "Il nest pas venu avant que je ne lappelle."}
{"text": "У всесвіті багато галактик.", "translation": "Il y a beaucoup de galaxies dans l'univers."}
{"text": "Вона приймає душ щоранку.", "translation": "Elle prend une douche chaque matin."}
{"text": "У Майка є декілька друзів у Флориді.", "translation": "Mike a quelques amis en Floride."}
{"text": "Я зустрінуся з тобою в неділю о третій.", "translation": "On se voit dimanche à trois heures."}
{"text": "Я сказав собі: «Це гарна ідея».", "translation": "Je me suis dit : « Cest une bonne idée. »"}
{"text": "Ми збиралися пробути там біля двох тижнів.", "translation": "Nous avions lintention de rester là près de deux semaines."}
{"text": "Я чищу зуби двічі на день.", "translation": "Je me brosse les dents deux fois par jour."}
{"text": "Він ніжно поклав руку на її плече.", "translation": "Il posa la main gentiment sur son épaule."}
{"text": "Сьогодні жахливо холодно.", "translation": "Il fait horriblement froid aujourd'hui."}
{"text": "У цю суму включено податки.", "translation": "Cette somme inclut les taxes."}
{"text": "Ця школа була заснована в 1650 році.", "translation": "Cette école fut fondée en 1650."}
{"text": "Я випадково знайшов цей ресторан.", "translation": "J'ai trouvé ce restaurant par hasard."}
{"text": "Я не хотів нікого образити.", "translation": "Je ne voulais vexer personne."}
{"text": "Цей сад найкраще виглядає весною.", "translation": "Ce parc est plus joli au printemps."}
{"text": "Цей сир виготовлено з овечого молока.", "translation": "Ce fromage est fait avec du lait de chèvre."}
{"text": "Він спить як немовля.", "translation": "Il dort comme un bébé."}
{"text": "Гора вкрита снігом.", "translation": "La montagne est recouverte de neige."}
{"text": "Я попав під дощ і промок.", "translation": "Jai été pris sous la pluie, et suis tout trempé."}
{"text": "Прошу, дайте мені ще один шанс.", "translation": "Je vous en prie, donnez-moi encore une chance."}
{"text": "Я все сказав.", "translation": "Jai tout dit."}
{"text": "Не забувай нас!", "translation": "Ne nous oublie pas !"}
{"text": "Випало багато снігу.", "translation": "Beaucoup de neige est tombée."}
{"text": "Йде сніг.", "translation": "Il est en train de neiger."}
{"text": "Може піти сніг.", "translation": "Il neigera peut-être."}
{"text": "У нас у січні йде сніг.", "translation": "Chez nous, il neige en janvier."}
{"text": "Сніг розтав.", "translation": "La neige a fondu."}
{"text": "Наша компанія планує побудувати новий хімічний завод у Росії.", "translation": "Notre entreprise a le projet de construire une nouvelle usine chimique en Russie."}
{"text": "Франція воювала з Росією.", "translation": "La France fut en guerre avec la Russie."}

170
Finetunning/validation.py Normal file
View File

@@ -0,0 +1,170 @@
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, GenerationConfig
from peft import PeftModel
from datasets import load_dataset
from nltk.translate.bleu_score import corpus_bleu
# ----------------------------
# Configuration
# ----------------------------
BASE_MODEL = "Qwen/Qwen2.5-7B-Instruct" # base model
LORA_DIR = "./qwen2.5-7b-uk-fr-lora-2epoch" # fine-tuned LoRA
VALIDATION_FILE = "validation.jsonl" # small validation subset
MAX_INPUT_LENGTH = 1024
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
# Liste des prompts à tester
PROMPTS_TO_TEST = [
{
"name": "Prompt de base",
"prompt": "Traduis la phrase ukrainienne suivante en français: {text}"
},
{
"name": "Prompt spécialisé mémoires",
"prompt": (
"Tu es un traducteur spécialisé dans les mémoires ukrainiennes des années 1910.\n"
"- Garde le style narratif et les tournures orales de l'auteur.\n"
"- Respecte les règles de traduction suivantes :\n\n"
"Règles strictes :\n"
"1. **Conserve tous les noms de lieux** dans leur forme originale (ex. : Львів → Lviv, mais ajoute une note si nécessaire entre [ ]).\n"
"2. **Respecte le style narratif** : garde les tournures orales et les expressions propres à l'auteur.\n\n"
"Voici la phrase à traduire :\nUkrainien : {text}\nFrançais :"
)
},
{
"name": "Prompt détaillé",
"prompt": (
"Tu es un expert en traduction littéraire spécialisé dans les textes historiques ukrainiens.\n"
"Règles à suivre absolument :\n"
"1. Conserve tous les noms propres et toponymes dans leur forme originale\n"
"2. Préserve le style et le registre de l'auteur original\n"
"3. Ajoute des notes entre crochets pour expliquer les références culturelles si nécessaire\n"
"4. Traduis de manière naturelle en français tout en restant fidèle au texte source\n\n"
"Texte à traduire :\nUkrainien : {text}\nTraduction française :"
)
},
{
"name": "Prompt minimaliste",
"prompt": "Traduction fidèle de l'ukrainien vers le français : {text}"
}
]
print("=== Loading tokenizer and model ===")
# ----------------------------
# Load tokenizer
# ----------------------------
tokenizer = AutoTokenizer.from_pretrained(
BASE_MODEL,
trust_remote_code=True
)
tokenizer.pad_token = tokenizer.eos_token
tokenizer.model_max_length = MAX_INPUT_LENGTH
# ----------------------------
# Load base model directly on GPU
# ----------------------------
print(f"{80 * '_'}\nLoading base model on GPU...")
base_model = AutoModelForCausalLM.from_pretrained(
BASE_MODEL,
torch_dtype=torch.float16,
device_map={"": 0}, # all on GPU
trust_remote_code=True
)
# ----------------------------
# Apply LoRA adapter
# ----------------------------
print(f"{80 * '_'}\nApplying LoRA adapter...")
model = PeftModel.from_pretrained(base_model, LORA_DIR)
model.eval()
model.to(DEVICE) # ensure everything on GPU
print("Model ready for validation.")
# ----------------------------
# Load validation dataset
# ----------------------------
print(f"{80 * '_'}\nLoading validation dataset...")
dataset = load_dataset("json", data_files=VALIDATION_FILE)
examples = dataset["train"]
print(f"{len(examples)} examples loaded for testing.")
# ----------------------------
# Translation function
# ----------------------------
@torch.inference_mode()
def translate(text, prompt_template):
prompt = prompt_template.format(text=text)
inputs = tokenizer(
prompt,
return_tensors="pt",
truncation=True,
max_length=MAX_INPUT_LENGTH
).to(DEVICE)
# Utilisation de GenerationConfig pour éviter les avertissements
generation_config = GenerationConfig.from_model_config(model.config)
generation_config.max_new_tokens = 256
generation_config.do_sample = False
outputs = model.generate(
**inputs,
generation_config=generation_config
)
result = tokenizer.decode(outputs[0], skip_special_tokens=True)
# Extraction de la partie traduction
if "Français :" in result:
translation_part = result.split("Français :")[-1].strip()
elif "Traduction française :" in result:
translation_part = result.split("Traduction française :")[-1].strip()
else:
translation_part = result.split(text)[-1].strip()
return translation_part
# ----------------------------
# Evaluate all prompts and select best BLEU
# ----------------------------
best_bleu = 0
best_prompt = None
all_results = {}
print(f"{80 * '_'}\nTesting all prompts and computing BLEU scores...")
for prompt_config in PROMPTS_TO_TEST:
print(f"\n{80 * '='}\nTesting prompt: {prompt_config['name']}\n{80 * '='}")
references = []
hypotheses = []
for i, example in enumerate(examples):
src_text = example["text"]
ref_text = example["translation"]
pred_text = translate(src_text, prompt_config["prompt"])
print(f"\n[{i+1}] Source: {src_text}")
print(f" Reference: {ref_text}")
print(f" Prediction: {pred_text}")
references.append([ref_text.split()])
hypotheses.append(pred_text.split())
bleu_score = corpus_bleu(references, hypotheses) * 100
print(f"\n=== Corpus BLEU score for '{prompt_config['name']}': {bleu_score:.4f} ===")
all_results[prompt_config["name"]] = bleu_score
if bleu_score > best_bleu:
best_bleu = bleu_score
best_prompt = prompt_config
# ----------------------------
# Display results
# ----------------------------
print(f"\n{80 * '='}\nFINAL RESULTS\n{80 * '='}")
for prompt_name, score in all_results.items():
print(f"{prompt_name}: {score:.4f}")
print(f"\nBEST PROMPT: {best_prompt['name']} with BLEU score: {best_bleu:.4f}")
print(f"Prompt content:\n{best_prompt['prompt']}")

View File

@@ -1,10 +0,0 @@
FROM lauchacarro/qwen2.5-translator
PARAMETER temperature 0.2
PARAMETER num_ctx 65536
SYSTEM """
Tu es un traducteur professionnel spécialisé dans la traduction de textes historiques ukrainiens en français.
Traduis avec précision et naturel, en respectant l'intonation originale utilisée par l'auteur du texte.
Tu dois toujours répondre en français et uniquement en français.
Tu ne dois pas interpréter les pensées ou les réflexions de l'auteur.
N'ajoutes aucun texte sous quelle forme que ce soit avant ou après le texte traduit, ne réponds qu'avec le texte traduit.
"""

149
README.md
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)
- **Ollama** installé et en cours d'exécution sur ta machine (en mode "serveur")
- 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
@@ -44,11 +21,16 @@ ollama create traductionUkrainienVersFrancais -f .\Modelfile
```
2. **Installer les dépendances Python**
```bash
pip install -r requirements.txt
```
```bash
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/cu121
```
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`)
---
@@ -57,37 +39,16 @@ ollama create traductionUkrainienVersFrancais -f .\Modelfile
### Préparation
1. **Démarrer Ollama en mode serveur** sur votre machine (port 11434 par défaut)
```bash
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."""
```bash
ollama serve
```
### Exécution
1. **Lancer le script de traduction**
```bash
python main.py
```
```bash
python main.py
```
2. **Le programme va :**
- Extraire le texte de toutes les pages du PDF
@@ -97,8 +58,8 @@ Preserve the layout and structure of the original text."""
- Envoyer chaque chunk au LLM pour traduction
- Afficher la progression dans le terminal
3. **Le résultat final** sera généré dans un fichier PDF nommé `[nom_original] (FR).pdf`
- Exemple : `TaniaBorecMemoir(Ukr) (FR).pdf`
3. **Le résultat final** sera généré dans un fichier PDF nommé `[nom_original](FR).pdf`
- Exemple : `TaniaBorecMemoir(Ukr)(FR).pdf`
### Fichier de sortie
@@ -120,18 +81,74 @@ Vous pouvez modifier les paramètres suivants dans `main.py` :
---
## Dépannage
## Finnetunning
/!\ Expérimental !!!
### 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`)
Le finne-tunning permet d'avoir une meilleur traduction. C'est un processus long en temps de calcul, mais permet une traduction plus précise.
### Erreur : "Modèle non trouvé"
- Exécutez : `ollama create traductionUkrainienVersFrancais -f .\Modelfile`
Le principe est le suivant :
### 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
```
1⃣ Dataset dentraînement (pairs.json)
1⃣ Dataset nettoyé ( cleanDataSet.py -> pairs_clean.json)
2⃣ Fine-tuning LoRA (finetuning.py)
3⃣ Validation / Évaluation BLEU (validation.py)
4⃣ Merge LoRA + modèle de base (mergeLora.py)
5⃣ Conversion en GGUF ()
6⃣ Ollama (inférence finale)
---
```
### Nettoyage du dataset
Executer le script ```python cleanDataSet.py```
### Validation
Executer le script ```python validation.py```
Le script tests plusieurs prompts et renvoie celui avec le meilleur score BLEU.
Il faut ensuite copier ce prompt dans le fichier ModelFile.
### Merge
Executer le script ```python mergeLora.py```
### Conversion en GGUF
En étant à la racine du projet (et toujours dans le venv), cloner le projet llama.cpp
```bash
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
pip install -r requirements.txt
```
Et lancer la commande (/!\ ca prend eviron 10 minutes):
```bash
python convert_hf_to_gguf.py ../Finetunning/qwen2.5-7b-uk-fr-merged --outfile qwen2.5-7b-uk-fr.gguf --outtype q8_0
```
Vérification :
```bash
./main -m qwen2.5-7b-uk-fr.gguf -p "Translate into French: Привіт світ"
```
Pour que ce nouveau modèle soit exploitable par ollama, il faut TODO
## Utilisation du modèle fine-tunné pour la traduction
Créer un Modelfile :
```
FROM ./qwen2.5-7b-uk-fr.gguf
PARAMETER temperature 0.1
PARAMETER top_p 0.95
PARAMETER num_ctx 4096
SYSTEM """
You are a professional Ukrainian to French translator.
Produce faithful, literal translations.
"""
```

Binary file not shown.

19
Traduction/Modelfile Normal file
View File

@@ -0,0 +1,19 @@
FROM translategemma:12b
PARAMETER temperature 0.2
PARAMETER num_ctx 8192
SYSTEM """
You are a professional Ukrainian (uk) to french (fr) translator. Your goal is to accurately convey the meaning and nuances of the original Ukrainian text while adhering to french grammar, vocabulary, and cultural sensitivities.
Produce only the french translation, without any additional explanations or commentary. Please translate the following Ukrainian text into french:
{TEXT}
Règles strictes :
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.
3. **Pour les termes historiques** (ex. : "powiat"), utilise le terme français standard et ajoute une note explicative.
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.
6. **Notes du traducteur** : Ajoute entre crochets [ ] les explications contextuelles si un contexte historique existe (ex. : "[Note : le context]").
"""

View File

@@ -8,22 +8,20 @@ from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_JUSTIFY
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
import os
from datetime import datetime
import os, time
# Configuration
PDF_PATH = "TaniaBorecMemoir(Ukr).pdf" # Fichier original
PDF_PATH = "Traduction/TaniaBorecMemoir(Ukr).pdf"
OLLAMA_MODEL = "traductionUkrainienVersFrancais:latest"
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.)
OLLAMA_URL = "http://localhost:11434/api/generate"
TARGET_LANGUAGE = "français"
CHECKPOINT_FILE = "Traduction/checkpoint.json"
TEMP_OUTPUT_TXT = "Traduction/output_temp.txt"
FINAL_OUTPUT_PDF = PDF_PATH.replace(".pdf",f"({TARGET_LANGUAGE.upper()[:2]})_V10.pdf")
FINAL_OUTPUT_TXT = PDF_PATH.replace(".pdf",f"({TARGET_LANGUAGE.upper()[:2]})_V10.txt")
DEBUG = True
# Récupère la date et l'heure actuelles au format AAAMMJJ-HHMM
current_datetime = datetime.now().strftime("%Y%m%d-%H%M")
# Ajoute la date et la langue cible au nom du fichier PDF de sortie
OUTPUT_PDF_PATH = PDF_PATH.replace(
".pdf",
f" ({TARGET_LANGUAGE.upper()[:2]})_{current_datetime}.pdf"
)
def extract_parameters_from_template(template_str):
"""Extrait les paramètres du modèle à partir du template."""
@@ -147,61 +145,9 @@ def display_llm_info():
else:
return "Informations du modèle non disponibles"
def extract_text_from_pdf(pdf_path):
"""Extrait le texte page par page d'un PDF sans les numéros de pages."""
import re
text_by_page = []
with open(pdf_path, "rb") as file:
reader = PyPDF2.PdfReader(file)
for page in reader.pages:
text = page.extract_text()
# Supprime les numéros de pages (nombres seuls en début/fin de ligne)
text = re.sub(r'^\d+\s*\n', '', text, flags=re.MULTILINE)
text = re.sub(r'\n\s*\d+\s*$', '', text, flags=re.MULTILINE)
text_by_page.append(text)
return text_by_page
def split_pages_in_paragraphs(pages_text):
"""
Divise le texte en paragraphes en détectant un point suivi d'un saut de ligne ou d'un retour à la ligne.
Conserve les sauts de ligne à l'intérieur des paragraphes.
"""
import re
# Concatène tout le texte
full_text = "\n".join(pages_text)
# Remplace les sauts de ligne à l'intérieur des paragraphes par des espaces
# (pour éviter les sauts de ligne intempestifs dans un même paragraphe)
full_text = re.sub(r'(?<![.!?])\n+(?![.!?])', ' ', full_text)
# Divise le texte en paragraphes : un point suivi d'un saut de ligne
paragraphs = re.split(r'(?<=[.!?])\s*\n+', full_text.strip())
# Nettoie chaque paragraphe : remplace les sauts de ligne restants par des espaces
paragraphs = [re.sub(r'\s+', ' ', p).strip() for p in paragraphs if p.strip()]
return paragraphs
def send_to_ollama(text, target_lang=TARGET_LANGUAGE, model=OLLAMA_MODEL, context_size=128000):
"""Envoie une requête à Ollama et retourne la réponse traduite."""
# Construit le prompt avec les instructions système et la demande de traduction
full_prompt = f"\n\nTraduis le texte suivant de l'ukrainien vers le {target_lang} :\n{text}"
payload = {
"model": model,
"prompt": full_prompt,
"stream": False,
"options": {"num_ctx": context_size}
}
response = requests.post(OLLAMA_URL, data=json.dumps(payload))
if response.status_code == 200:
return response.json()["response"]
else:
raise Exception(f"Erreur Ollama: {response.text}")
def register_unicode_font():
"""Enregistre une police TrueType qui supporte le cyrilique."""
# Recherche une police système qui supporte le cyrilique
"""Enregistre une police TrueType qui supporte le cyrillique."""
# Recherche une police système qui supporte le cyrillique
font_paths = [
r"C:\Windows\Fonts\DejaVuSans.ttf",
r"C:\Windows\Fonts\Calibri.ttf",
@@ -220,15 +166,64 @@ def register_unicode_font():
print("Aucune police Unicode trouvée, utilisation d'Helvetica")
return 'Helvetica'
def create_pdf_from_results(results, output_path):
"""Crée un PDF à partir des résultats de traduction."""
doc = SimpleDocTemplate(output_path, pagesize=letter, topMargin=inch, bottomMargin=inch)
story = []
# Charge ou initialise le checkpoint
def load_checkpoint():
if os.path.exists(CHECKPOINT_FILE):
with open(CHECKPOINT_FILE, "r") as f:
return json.load(f)
return {"last_processed_index": -1, "results": {}}
# Enregistre une police qui supporte le cyrilique
# Sauvegarde le checkpoint
def save_checkpoint(last_index, results):
# Trier les clés du dictionnaire results
sorted_results = {key: results[key] for key in sorted(results.keys(), key=int)}
with open(CHECKPOINT_FILE, "w") as f:
# Utiliser un espace d'indentation de 4 espaces pour rendre le JSON plus lisible
json.dump({"last_processed_index": last_index, "results": sorted_results}, f, indent=4)
# Sauvegarde les résultats temporaires dans un fichier TXT
def save_temp_results(results):
with open(TEMP_OUTPUT_TXT, "w", encoding="utf-8") as f:
for idx, translation in results.items():
f.write(f"Paragraphe {idx}:\n{translation}\n\n")
# Extraction du texte du PDF (inchangée)
def extract_text_from_pdf(pdf_path):
text_by_page = []
with open(pdf_path, "rb") as file:
reader = PyPDF2.PdfReader(file)
for page in reader.pages:
text = page.extract_text()
text_by_page.append(text)
return text_by_page
# Découpage en paragraphes (inchangé)
def split_pages_in_paragraphs(pages_text):
import re
full_text = "\n".join(pages_text)
full_text = re.sub(r'(?<![.!?])\n+(?![.!?])', ' ', full_text)
paragraphs = re.split(r'(?<=[.!?])\s*\n+', full_text.strip())
paragraphs = [re.sub(r'\s+', ' ', p).strip() for p in paragraphs if p.strip()]
return paragraphs
# Envoi à Ollama (inchangé)
def send_to_ollama(text, target_lang=TARGET_LANGUAGE, model=OLLAMA_MODEL):
full_prompt = f"\n\nTraduis le texte suivant de l'ukrainien vers le {target_lang} :\n{text}"
payload = {"model": model, "prompt": full_prompt, "stream": False}
response = requests.post(OLLAMA_URL, data=json.dumps(payload))
if response.status_code == 200:
return response.json()["response"]
else:
raise Exception(f"Erreur Ollama: {response.text}")
# Création du PDF final avec numéros de chapitres dans la marge
def create_pdf_from_results(results, output_path):
"""Crée un PDF à partir des résultats de traduction, avec des notes dans la marge et un numéro de page."""
story = []
font_name = register_unicode_font()
# Style personnalisé
# Styles personnalisés
styles = getSampleStyleSheet()
title_style = ParagraphStyle(
'CustomTitle',
@@ -259,17 +254,34 @@ def create_pdf_from_results(results, output_path):
fontName=font_name
)
note_style = ParagraphStyle(
'CustomBody',
parent=styles['BodyText'],
fontSize=8,
alignment=TA_JUSTIFY,
spaceAfter=0,
fontName=font_name
)
# Création du document avec les callbacks pour les notes et le numéro de page
doc = SimpleDocTemplate(
output_path,
pagesize=letter,
topMargin=inch,
bottomMargin=inch,
)
# Titre avec la langue cible
story.append(Paragraph(f"Traduction - Ukrainien vers {TARGET_LANGUAGE.capitalize()}", title_style))
story.append(Paragraph(f"Document : {PDF_PATH}", title_style))
story.append(Spacer(1, 0.2*inch))
# Contenu
for page_num, translation in results.items():
# Préserver la mise en page en convertissant les sauts de ligne
for paragraph_num, translation in results.items():
formatted_text = translation.replace("\n", "<br/>")
if DEBUG:
# Ajoute le paragraphe avec sa note
story.append(Paragraph(paragraph_num, note_style))
story.append(Paragraph(formatted_text, body_style))
# story.append(Spacer(1, 0.1*inch))
# Infos sur le LLM
story.append(Spacer(1, 0.2*inch))
@@ -279,6 +291,7 @@ def create_pdf_from_results(results, output_path):
doc.build(story)
print(f"PDF généré avec succès : {output_path}")
def create_txt_from_results(results, output_path):
"""Crée un fichier TXT à partir des résultats de traduction."""
OUTPUT_TXT_PATH = output_path.replace(".pdf", f".txt") # Chemin du fichier TXT de sortie
@@ -289,7 +302,10 @@ def create_txt_from_results(results, output_path):
txt_file.write(title_text + "\n\n")
# Contenu
for page_num, translation in results.items():
for paragraph_num, translation in results.items():
# Ajoute les numéro de paragraphe et chapitre
if(DEBUG): txt_file.write(f"{paragraph_num}\n")
# Préserver la mise en page en convertissant les sauts de ligne
txt_file.write(translation + "\n\n")
@@ -297,35 +313,93 @@ def create_txt_from_results(results, output_path):
txt_file.write("\n")
txt_file.write(display_llm_info() + "\n")
# Fonction principale
def main():
# Affiche les informations du modèle LLM
display_llm_info()
checkpoint = load_checkpoint()
last_index = checkpoint["last_processed_index"]
results = checkpoint["results"]
# Extraction du texte page par page
pages = extract_text_from_pdf(PDF_PATH)
print(f"Nombre de pages extraites : {len(pages)}")
# Fusion des paragraphes qui s'étendent sur plusieurs pages
paragraphs = split_pages_in_paragraphs(pages)
print(f"Nombre de paragraphes complets extraits : {len(paragraphs)}")
# Dictionnaire pour stocker les résultats
results = {}
# Liste de tous les indices de batches attendus (par pas de batch_size)
batch_size = 5
expected_batch_indices = list(range(0, len(paragraphs), batch_size))
# Liste des indices de batches déjà présents dans results
present_batch_indices = set()
for key in results.keys():
batch_start = int(int(key) // batch_size * batch_size) # Arrondit à l'indice de début de batch
present_batch_indices.add(batch_start)
# Trouve les batches manquants
missing_batches = [i for i in expected_batch_indices if i not in present_batch_indices and i <= last_index]
# Affichage des batches manquants (pour débogage)
print(f"Batches manquants détectés : {missing_batches}")
# Traduction des paragraphes manquants
temps_cumule = 0.0
for i in missing_batches:
batch = paragraphs[i:i + batch_size]
paragraph_cumul = "\n".join(batch)
print(f"{15 * '-'} Traduction des paragraphes manquants {i+1} à {min(i + batch_size, len(paragraphs))} / {len(paragraphs)}")
# Traitement des paragraphes complets
for i, paragraph_text in enumerate(paragraphs, start=1):
print(f"{15 * '-'} Traduction du paragraphe {i}/{len(paragraphs)}...")
try:
result = send_to_ollama(paragraph_text, target_lang=TARGET_LANGUAGE)
print(f"{result}.")
results[i] = result
except Exception as e:
print(f"Erreur lors du traitement du paragraphe {i} : {e}")
results[i] = f"Erreur lors du traitement du paragraphe {i} : {e}"
debut_chrono = time.time()
result = send_to_ollama(paragraph_cumul)
fin_chrono = time.time()
temps_paragraphe = fin_chrono - debut_chrono
temps_cumule += temps_paragraphe
# Conversion en minutes et secondes
minutes_paragraphe, secondes_paragraphe = divmod(temps_paragraphe, 60)
minutes_cumule, secondes_cumule = divmod(temps_cumule, 60)
print(f"{result}")
results[str(i)] = result
save_checkpoint(len(paragraphs), results) # Met à jour le dernier indice du batch
save_temp_results(results)
except Exception as e:
print(f"Erreur lors de la traduction du paragraphe {i}: {e}")
print(f" Temps de traduction : {int(minutes_paragraphe)} min {secondes_paragraphe:.2f} sec")
print(f" Temps cumulé : {int(minutes_cumule)} min {secondes_cumule:.2f} sec")
# Traitement des paragraphes suivants
for i in range(last_index + 1, len(paragraphs), batch_size):
batch = paragraphs[i:i + batch_size]
paragraph_cumul = "\n".join(batch)
print(f"{15 * '-'} Traduction des paragraphes {i+1} à {min(i + batch_size, len(paragraphs))} / {len(paragraphs)}")
try:
debut_chrono = time.time()
result = send_to_ollama(paragraph_cumul)
fin_chrono = time.time()
temps_paragraphe = fin_chrono - debut_chrono
temps_cumule += temps_paragraphe
# Conversion en minutes et secondes
minutes_paragraphe, secondes_paragraphe = divmod(temps_paragraphe, 60)
minutes_cumule, secondes_cumule = divmod(temps_cumule, 60)
print(f"{result}")
results[str(i)] = result
save_checkpoint(i + batch_size - 1, results)
save_temp_results(results)
except Exception as e:
print(f"Erreur : {e}")
continue
print(f" Temps de traduction : {int(minutes_paragraphe)} min {secondes_paragraphe:.2f} sec")
print(f" Temps cumulé : {int(minutes_cumule)} min {secondes_cumule:.2f} sec")
save_temp_results(results)
create_pdf_from_results(results, FINAL_OUTPUT_PDF)
create_txt_from_results(results, FINAL_OUTPUT_TXT)
print("Traduction terminée !")
# Création du PDF avec tous les résultats
create_pdf_from_results(results, OUTPUT_PDF_PATH)
create_txt_from_results(results, OUTPUT_PDF_PATH)
if __name__ == "__main__":
main()

1
llama.cpp Submodule

Submodule llama.cpp added at e463bbdf65

View File

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

46
run.bat Normal file
View File

@@ -0,0 +1,46 @@
@echo off
setlocal
set OLLAMA_PORT=11434
set OLLAMA_HOST=localhost
REM Récupérer le répertoire courant
set CURRENT_DIR=%cd%
REM Chemin vers l'environnement virtuel Python (relatif au répertoire courant)
set VENV_PATH=%CURRENT_DIR%\venv
REM Chemin vers votre script principal (relatif au répertoire courant)
set MAIN_SCRIPT_PATH=%CURRENT_DIR%\Traduction\main.py
REM Activer l'environnement virtuel Python
call %VENV_PATH%\Scripts\activate.bat
REM Lancer la compilation du modèle LLM pour Ollama
echo Compilation du modèle LLM pour Ollama
ollama create traductionUkrainienVersFrancais -f .\Traduction\Modelfile
:: 1. Vérifie si le processus ollama.exe est en cours d'exécution
tasklist | find "ollama.exe" >nul
if %ERRORLEVEL% equ 0 (
echo [OK] Le processus Ollama est bien en cours d'execution.
) else (
echo [ERREUR] Ollama n'est pas lancé.
pause
exit /b 1
)
:: 2. Vérifie si le port 11434 est ouvert en local
echo Verification du port %OLLAMA_PORT% sur %OLLAMA_HOST%...
powershell -Command "$tcp = New-Object System.Net.Sockets.TcpClient; $connect = $tcp.ConnectAsync('%OLLAMA_HOST%', %OLLAMA_PORT%); $connect.Wait(1000); if ($tcp.Connected) { echo 'Port %OLLAMA_PORT% ouvert en local.'; $tcp.Close() } else { echo 'Port %OLLAMA_PORT% non accessible en local.'; exit 1 }"
if %ERRORLEVEL% neq 0 (
echo [ERREUR] Le port %OLLAMA_PORT% n'est pas accessible en local.
pause
exit /b 1
)
REM Exécuter le script principal
echo Lancement du script principal de traduction
python %MAIN_SCRIPT_PATH%
endlocal