{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":121150,"databundleVersionId":14976537,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":14229611,"sourceType":"datasetVersion","datasetId":9077938},{"sourceId":14261138,"sourceType":"datasetVersion","datasetId":9100067},{"sourceId":14267119,"sourceType":"datasetVersion","datasetId":9104351},{"sourceId":14291558,"sourceType":"datasetVersion","datasetId":9122579},{"sourceId":693696,"sourceType":"modelInstanceVersion","isSourceIdPinned":false,"modelInstanceId":526045,"modelId":540104},{"sourceId":694236,"sourceType":"modelInstanceVersion","isSourceIdPinned":false,"modelInstanceId":526467,"modelId":540514},{"sourceId":694614,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":526756,"modelId":540799},{"sourceId":694416,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":526604,"modelId":540649}],"dockerImageVersionId":31236,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport pandas as pd\nimport torch\nimport math\nfrom transformers import AutoTokenizer, AutoModelForSeq2SeqLM, AutoModelForCausalLM\nfrom tqdm.auto import tqdm\n\n# --- 1. CONFIGURATION ---\nBASE_MODEL_PATH = \"/kaggle/input/akkad-byt5-model-v3-finetune-3epochs\"\nBATCH_SIZE = 4\nMAX_LENGTH = 512\nDEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\nprint(f\"⚙️ Environnement : {DEVICE}\")\n\n# --- 2. LOCALISATION INTELLIGENTE DU MODÈLE ---\nmodel_path = BASE_MODEL_PATH\nfor root, dirs, files in os.walk(BASE_MODEL_PATH):\n    if \"config.json\" in files:\n        model_path = root\n        break\n\nprint(f\"📂 Modèle détecté dans : {model_path}\")\n\n# --- 3. CHARGEMENT DES MODÈLES ---\n\n# A. Modèle de Traduction (ByT5)\nprint(\"⏳ Chargement du Traducteur (ByT5)...\")\ntokenizer = AutoTokenizer.from_pretrained(model_path)\nmodel = AutoModelForSeq2SeqLM.from_pretrained(model_path).to(DEVICE)\nmodel.eval()\n\n# ==============================================================================\n# 3. LE JUGE HEURISTIQUE (ANTI-RÉPÉTITION & OFFLINE)\n# ==============================================================================\nclass PythonHeuristicJudge:\n    def __init__(self):\n        pass \n\n    def score(self, sentence):\n        \"\"\"\n        Calcule une pénalité. \n        Plus le score est HAUT, plus la phrase est MAUVAISE.\n        \"\"\"\n        clean_sent = sentence.strip()\n        words = clean_sent.split()\n        \n        # 1. Hallucinations vides (toujours à rejeter)\n        if not words: return 9999.0\n        \n        # 2. Ratio de Diversité (Le Juge Anti-Boucle)\n        # Ex: \"King King King\" -> Ratio 0.33 -> Pénalité énorme\n        # Ex: Phrase de 70 mots variée -> Ratio 0.8 -> Pénalité faible\n        unique_words = set([w.lower() for w in words])\n        diversity_ratio = len(unique_words) / len(words) if len(words) > 0 else 0\n        \n        # Pénalité explosive si le vocabulaire est pauvre\n        penalty_repetition = (1.0 - diversity_ratio) * 500.0 \n        \n        # 3. Pénalité de Longueur (Ajustée pour tes phrases de 70 mots)\n        # On ne commence à punir qu'à partir de 100 mots.\n        penalty_length = 0\n        if len(words) > 100:\n            penalty_length = (len(words) - 100) * 10 \n        \n        # 4. Pénalité Mots Interdits (Boucles connues)\n        penalty_keywords = 0\n        text_lower = clean_sent.lower()\n        if \"of the land of the land\" in text_lower: penalty_keywords += 200\n        # On punit les triple répétitions \"King of the king of the king\"\n        if \"king of the king of the king\" in text_lower: penalty_keywords += 200\n\n        return penalty_repetition + penalty_length + penalty_keywords\n\n    def pick_best(self, candidates):\n        scored_candidates = []\n        for cand in candidates:\n            s = self.score(cand)\n            scored_candidates.append((s, cand))\n        \n        # On trie : le plus petit score (la phrase la plus propre) gagne\n        scored_candidates.sort(key=lambda x: x[0])\n        \n        # Le premier de la liste est le meilleur\n        return scored_candidates[0][1]\n\n# Activation du Juge\njudge = PythonHeuristicJudge()\nprint(\"🛡️ Juge Heuristique activé (Tolérance Longueur: 100 mots).\")\n# --- 4. CHARGEMENT DES DONNÉES DE TEST ---\ncomp_dir = \"/kaggle/input/deep-past-initiative-machine-translation\"\nif not os.path.exists(comp_dir):\n    input_dirs = [d for d in os.listdir(\"/kaggle/input\") if \"deep-past\" in d or \"translation\" in d]\n    if input_dirs:\n        comp_dir = os.path.join(\"/kaggle/input\", input_dirs[0])\n\ntest_file = os.path.join(comp_dir, \"test.csv\")\nprint(f\"📄 Chargement du fichier test : {test_file}\")\ntest_df = pd.read_csv(test_file)\n\n# --- 5. MOTEUR DE TRADUCTION AVEC RERANKING ---\ndef translate_batch_with_reranking(texts, batch_size=4):\n    final_translations = []\n    # Préfixe (Si ton modèle a été entraîné avec)\n    inputs_text = [\"translate Akkadian to English: \" + str(t).strip() for t in texts]\n    \n    # Nombre de candidats par phrase\n    N_CANDIDATES = 10\n    \n    for i in tqdm(range(0, len(inputs_text), batch_size), desc=\"Traduction + Vote\"):\n        batch = inputs_text[i : i + batch_size]\n        \n        inputs = tokenizer(\n            batch, \n            return_tensors=\"pt\", \n            padding=True, \n            truncation=True, \n            max_length=MAX_LENGTH\n        ).to(DEVICE)\n        \n        with torch.no_grad():\n            # On génère 5 versions pour CHAQUE phrase du batch\n            generated_ids = model.generate(\n                **inputs,\n                max_length=512,\n                num_beams=N_CANDIDATES,             # Beam Search large\n                num_return_sequences=N_CANDIDATES,  # On veut voir les 5 options\n                repetition_penalty=1.2,  # ⚠️ PÉNALITÉ FORTE pour éviter les boucles à la source\n                early_stopping=True\n            )\n        \n        # Décodage de tous les candidats\n        decoded_candidates = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)\n        \n        # decoded_candidates est une liste plate [P1_v1, P1_v2, ..., P1_v5, P2_v1, ...]\n        # Il faut les regrouper par paquet de 5 pour faire voter le juge\n        \n        for j in range(0, len(decoded_candidates), N_CANDIDATES):\n            # On prend les 5 versions de la phrase actuelle\n            group_of_5 = decoded_candidates[j : j + N_CANDIDATES]\n            \n            # Le juge choisit la meilleure (celle qui ne bugge pas)\n            best_sent = judge.pick_best(group_of_5)\n            final_translations.append(best_sent)\n            \n    return final_translations\n\n# --- 6. EXÉCUTION ---\nprint(f\"🔥 Démarrage de la traduction (Best-of-5) de {len(test_df)} phrases...\")\n# Attention : batch_size réduit car on génère 5x plus de texte en mémoire\ntest_df['translation'] = translate_batch_with_reranking(test_df['transliteration'].tolist(), batch_size=4)\n\n# --- 7. SAUVEGARDE ---\nsubmission = test_df[['id', 'translation']]\nsubmission.to_csv(\"/kaggle/working/submission.csv\", index=False)\n\nprint(\"\\n\" + \"=\"*40)\nprint(\"✅ submission.csv GÉNÉRÉ AVEC SUCCÈS (Nettoyé par GPT-2) !\")\nprint(\"👉 Tu peux cliquer sur 'Submit' à droite.\")\nprint(\"=\"*40)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#2323","metadata":{}}]}