{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":116062,"databundleVersionId":14084779,"sourceType":"competition"}],"dockerImageVersionId":31154,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# À exécuter une seule fois dans votre environnement\n!pip install torch transformers sentencepiece biopython\n!pip install obonet\n\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport numpy as np\nfrom Bio import SeqIO\nimport obonet\nimport networkx as nx\n\n# --- Configuration des chemins de fichiers (CORRIGÉ) ---\nBASE_PATH = '/kaggle/input/cafa-6-protein-function-prediction/'\nPATHS = {\n    'train_terms': BASE_PATH + 'Train/train_terms.tsv',\n    'ia_weights': BASE_PATH + 'IA.tsv',\n    'train_taxonomy': BASE_PATH + 'Train/train_taxonomy.tsv',\n    'train_sequences': BASE_PATH + 'Train/train_sequences.fasta',\n    # CORRECTION APPLIQUÉE : ajout du sous-répertoire 'Train/'\n    'go_obo': BASE_PATH + 'Train/go-basic.obo' \n}\n\n# Noms de colonnes pour la robustesse:\nTERM_COL = 'term' \nPROTEIN_COL = 'EntryID'\nTAXON_COL = 'TaxonID'\nGO_MERGE_COL = 'GO_ID_MERGE' \nWEIGHT_COL_NAME = 'IA_WEIGHT' \n\n# --- 1. Chargement des Fichiers TSV ---\ntry:\n    print(\"--- 1. Chargement des données TSV ---\")\n    \n    # Chargement standard des termes (contient un en-tête)\n    train_terms = pd.read_csv(PATHS['train_terms'], sep='\\t')\n    \n    # Chargement IA sans en-tête\n    ia_weights = pd.read_csv(PATHS['ia_weights'], sep='\\t', header=None, names=[GO_MERGE_COL, WEIGHT_COL_NAME])\n    \n    # Chargement Taxonomie sans en-tête\n    train_taxonomy = pd.read_csv(PATHS['train_taxonomy'], sep='\\t', header=None, names=[PROTEIN_COL, TAXON_COL])\n\n    print(f\"train_terms.tsv chargé. Colonnes : {list(train_terms.columns)}\")\n    print(f\"ia_weights.tsv chargé. Colonnes : {list(ia_weights.columns)}\")\n    print(f\"train_taxonomy.tsv chargé. Colonnes : {list(train_taxonomy.columns)}\")\n\nexcept FileNotFoundError as e:\n    print(f\"\\nERREUR: Fichier introuvable. Veuillez vérifier vos chemins (paths).\\nErreur: {e}\")\n    exit()\n\n# ----------------------------------------------------------------------\n## 2. Analyse des Labels et des Poids d'Évaluation\n# ----------------------------------------------------------------------\nprint(\"\\n\" + \"=\"*50)\nprint(\"--- 2. ANALYSE DES LABELS ET DES POIDS D'ÉVALUATION ---\")\nprint(\"=\"*50)\n\n# --- A. Fréquence des Termes GO (Déséquilibre des Classes) ---\nprint(\"\\n--- A. Fréquence des Termes GO (colonne 'term') ---\")\nterm_counts = train_terms[TERM_COL].value_counts()\nn_unique_terms = len(term_counts)\n\nprint(f\"Nombre total de termes GO uniques (classes): {n_unique_terms}\")\nprint(f\"Termes les plus fréquents (Tête de la traîne):\\n{term_counts.head(5)}\")\nprint(f\"Termes les moins fréquents (annotations uniques):\\n{term_counts.tail(5)}\")\n\n# Visualisation du déséquilibre (log-log plot)\nplt.figure(figsize=(10, 6))\nplt.loglog(term_counts.values, marker='.', linestyle='')\nplt.title('Distribution des Fréquences des Termes GO (Log-Log)')\nplt.xlabel(f'Rang du Terme (Total: {n_unique_terms} classes)')\nplt.ylabel('Nombre de Protéines Annotées')\nplt.grid(True, which=\"both\", ls=\"--\")\nplt.show()\n\n# --- B. Annotations par Protéine ---\nprotein_counts = train_terms[PROTEIN_COL].value_counts()\nprint(\"\\n--- B. Annotations par Protéine (Classification Multilabel) ---\")\nprint(f\"Moyenne d'annotations par protéine: {protein_counts.mean():.2f}\")\nprint(f\"Médiane d'annotations par protéine: {protein_counts.median():.0f}\")\nprint(f\"Maximum d'annotations pour une seule protéine: {protein_counts.max()}\")\n\n\n# --- C. Lien entre Fréquence et Poids IA ---\nprint(\"\\n--- C. Poids d'Information (IA) : Importance vs. Rareté ---\")\n\nprint(f\"Statistiques sur les poids IA (score à atteindre > 0.5):\\n{ia_weights[WEIGHT_COL_NAME].describe()}\")\n\n# Fusionner les fréquences et les poids pour voir la corrélation\n# Correction: Renommer la colonne 'term' en 'GO_ID_MERGE' pour la fusion\nterm_freq_df = term_counts.rename('Frequency').reset_index().rename(columns={'term': GO_MERGE_COL})\n\n# La fusion est maintenant propre et basée sur la colonne 'GO_ID_MERGE'\nia_analysis = term_freq_df.merge(ia_weights, on=GO_MERGE_COL)\n\n# Visualisation (Fréquence vs IA)\nplt.figure(figsize=(10, 6))\nsns.scatterplot(x='Frequency', y=WEIGHT_COL_NAME, data=ia_analysis, alpha=0.6)\nplt.xscale('log') \nplt.title('Poids IA vs. Fréquence des Termes GO (Importance des termes rares)')\nplt.xlabel('Fréquence du Terme (échelle log)')\nplt.ylabel('Poids d\\'Information Accretion (IA)')\nplt.grid(True, which=\"both\", ls=\"--\")\nplt.show()\n\n\n# ----------------------------------------------------------------------\n## 3. Analyse des Séquences et Taxonomie\n# ----------------------------------------------------------------------\nprint(\"\\n\" + \"=\"*50)\nprint(\"--- 3. ANALYSE DES SÉQUENCES ET DE LA TAXONOMIE ---\")\nprint(\"=\"*50)\n\n# --- A. Analyse des Séquences (Longueur) ---\nprint(\"\\n--- A. Analyse des Séquences (train_sequences.fasta) ---\")\ntry:\n    sequences = list(SeqIO.parse(PATHS['train_sequences'], 'fasta'))\n    lengths = [len(seq.seq) for seq in sequences]\n    \n    print(f\"Nombre total de séquences d'entraînement: {len(lengths)}\")\n    print(f\"Longueur moyenne: {np.mean(lengths):.2f} acides aminés\")\n    print(f\"Longueur médiane: {np.median(lengths):.0f} acides aminés\")\n    print(f\"Longueur max/min: {np.max(lengths)} / {np.min(lengths)}\")\n\n    plt.figure(figsize=(10, 6))\n    sns.histplot(lengths, bins=50, kde=True)\n    plt.title('Distribution des Longueurs de Séquences de Protéines')\n    plt.xlabel('Longueur de la Séquence')\n    plt.ylabel('Fréquence')\n    plt.show()\n    \nexcept FileNotFoundError:\n    print(f\"Alerte: Le fichier {PATHS['train_sequences']} est manquant, analyse de séquence ignorée.\")\n    \n\n# --- B. Analyse de la Taxonomie (Distribution des Espèces) ---\nprint(\"\\n--- B. Analyse Taxonomique (Distribution par Espèce) ---\")\ntaxon_counts = train_taxonomy[TAXON_COL].value_counts()\nprint(f\"Nombre total d'espèces (Taxon IDs) uniques: {len(taxon_counts)}\")\nprint(f\"Top 5 des Taxons les plus fréquents (potentiel biais d'espèce):\\n{taxon_counts.head(5)}\")\n\n# Visualisation de la dominance des espèces\nplt.figure(figsize=(10, 6))\ntaxon_counts.head(10).plot(kind='bar')\nplt.title('Top 10 des Taxons (Espèces) dans le Jeu d\\'Entraînement')\nplt.xlabel('Taxon ID')\nplt.ylabel('Nombre de Protéines')\nplt.xticks(rotation=45, ha='right')\nplt.show()\n\n# ----------------------------------------------------------------------\n## 4. Analyse de la Gene Ontology (Structure du Graphe) (CORRIGÉ)\n# ----------------------------------------------------------------------\nprint(\"\\n\" + \"=\"*50)\nprint(\"--- 4. ANALYSE DE LA GENE ONTOLOGY (OBO) ---\")\nprint(\"=\"*50)\n\ntry:\n    graph = obonet.read_obo(PATHS['go_obo'])\n    print(\"Fichier OBO chargé avec succès.\")\n\n    # --- A. Structure du Graphe ---\n    print(f\"Nombre total de termes (nœuds) dans GO: {graph.number_of_nodes()}\")\n    print(f\"Nombre total de relations (arêtes) dans GO: {graph.number_of_edges()}\")\n\n    # --- B. Distribution par Sous-Ontologie (Aspect) ---\n    namespaces = [data.get('namespace') for node, data in graph.nodes(data=True)]\n    namespace_counts = pd.Series(namespaces).value_counts()\n    \n    print(\"\\nDistribution des termes par sous-ontologie ('aspect'):\")\n    print(namespace_counts)\n    \nexcept FileNotFoundError:\n    print(f\"\\n!!! ERREUR : Le fichier {PATHS['go_obo']} n'a pas été trouvé. !!!\")\n    print(\"Vérifiez une dernière fois l'existence du fichier et le chemin.\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport torch\nfrom transformers import AutoTokenizer, AutoModel\nfrom Bio import SeqIO\nfrom tqdm import tqdm\nimport os\nimport gc\nfrom huggingface_hub import snapshot_download\n\n# --- Configuration et Chemins (CORRIGÉ) ---\nBASE_PATH = '/kaggle/input/cafa-6-protein-function-prediction/'\n\n# 🛑 CORRECTION MAJEURE: Le chemin ne doit contenir que le fichier cible.\n# Nous ciblons le FASTA de test superset (les 224309 protéines).\nFASTA_PATH_TEST = BASE_PATH + 'Test/testsuperset.fasta'\n\n# Les noms de fichiers de sortie sont corrects\nOUTPUT_EMBEDDINGS_PATH_TEST = 'test_protein_embeddings_complet.npy'\nOUTPUT_IDS_PATH_TEST = 'test_protein_ids_complet.npy'\n\nMODEL_NAME = \"facebook/esm2_t6_8M_UR50D\"\nCHUNK_SIZE = 5\n\ndef generate_and_save_embeddings(fasta_path, model_name, output_emb_path, output_ids_path, chunk_size):\n    \n    print(f\"Chargement du modèle et du tokenizer : {model_name}\")\n    \n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    print(f\"Utilisation du dispositif : {device}\")\n\n    # --- Étape A : Téléchargement forcé dans un cache local stable ---\n    print(\"Tentative de téléchargement du modèle dans un cache local stable...\")\n    try:\n        # Cette fonction télécharge tous les fichiers dans un dossier temporaire et retourne le chemin\n        cache_dir = snapshot_download(\n            repo_id=model_name,\n            allow_patterns=[\"*.json\", \"*.bin\", \"*.safetensors\", \"*.txt\"],\n            local_files_only=False\n        )\n        print(f\"Modèle téléchargé/mis en cache avec succès à : {cache_dir}\")\n        \n    except Exception as e:\n        print(f\"ERREUR BLOQUANTE : Impossible de télécharger le modèle ou de trouver un cache stable. Erreur: {e}\")\n        return\n\n    # --- Étape B : Chargement à partir du cache local connu ---\n    try:\n        # On utilise le chemin du cache_dir connu pour charger le modèle et le tokenizer\n        tokenizer = AutoTokenizer.from_pretrained(cache_dir)\n        model = AutoModel.from_pretrained(cache_dir).to(device)\n        model.eval()\n        \n    except Exception as e:\n        print(f\"ERREUR FATALE : Impossible de charger le modèle depuis le cache. Erreur : {e}\")\n        return\n        \n    # --- Reste du script (inchangé) ---\n\n    sequences_data = []\n    \n    # Le chemin FASTA est maintenant correctement ciblé et utilisé ici\n    for record in SeqIO.parse(fasta_path, \"fasta\"):\n        processed_sequence = \" \".join(list(str(record.seq)))\n        sequences_data.append({\n            'id': record.id,\n            'sequence': processed_sequence\n        })\n\n    total_sequences = len(sequences_data)\n    all_embeddings = []\n    all_ids = []\n\n    print(f\"Début de la génération des embeddings pour {total_sequences} séquences avec CHUNK_SIZE={chunk_size}...\")\n    \n    for i in tqdm(range(0, total_sequences, chunk_size), desc=\"Génération des embeddings\"):\n        chunk = sequences_data[i:i + chunk_size]\n        \n        ids = [item['id'] for item in chunk]\n        sequences = [item['sequence'] for item in chunk]\n        \n        # Tokenisation : Troncature activée et padding au max de la batch\n        inputs = tokenizer(sequences, \n                           return_tensors=\"pt\", \n                           padding=True, \n                           truncation=True, \n                           max_length=1024)\n        \n        inputs = {k: v.to(device) for k, v in inputs.items()}\n        \n        with torch.no_grad():\n            outputs = model(**inputs)\n        \n        token_embeddings = outputs.last_hidden_state\n        attention_mask = inputs['attention_mask']\n        \n        # Calcul de la moyenne des tokens (embedding de séquence fixe)\n        sum_embeddings = torch.sum(token_embeddings * attention_mask.unsqueeze(-1), dim=1)\n        sum_mask = torch.sum(attention_mask, dim=1).unsqueeze(-1)\n        mean_embeddings = sum_embeddings / sum_mask\n        \n        all_embeddings.append(mean_embeddings.cpu().numpy())\n        all_ids.extend(ids)\n        \n        # Gestion de mémoire\n        del inputs, outputs, token_embeddings, mean_embeddings\n        if device.type == 'cuda':\n            torch.cuda.empty_cache()\n        gc.collect()\n\n    final_embeddings = np.concatenate(all_embeddings, axis=0)\n\n    # Sauvegarde\n    np.save(output_emb_path, final_embeddings)\n    np.save(output_ids_path, np.array(all_ids))\n    \n    print(\"\\n\" + \"=\"*50)\n    print(\"FIN DE L'ÉTAPE 1 : GÉNÉRATION DES EMBEDDINGS\")\n    print(f\"Embeddings sauvegardés dans : {output_emb_path}\")\n    print(f\"Dimensions : {final_embeddings.shape}\")\n    print(\"=\"*50)\n\n\n# 🛑 CORRECTION DE L'EXÉCUTION : Utiliser les chemins de TEST\ngenerate_and_save_embeddings(\n    fasta_path=FASTA_PATH_TEST, # <-- Utilise le chemin du superset de test\n    model_name=MODEL_NAME,\n    output_emb_path=OUTPUT_EMBEDDINGS_PATH_TEST, # <-- Utilise le chemin d'output de test\n    output_ids_path=OUTPUT_IDS_PATH_TEST,       # <-- Utilise le chemin d'output de test\n    chunk_size=CHUNK_SIZE\n)","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}