import subprocess import sys import gradio as gr import numpy as np # Installation PyTorch (plus fiable que TensorFlow) try: import torch import torch.nn as nn import torch.optim as optim from collections import Counter print(f"PyTorch {torch.__version__} déjà disponible") except ImportError: print("Installation de PyTorch...") subprocess.check_call([ sys.executable, "-m", "pip", "install", "torch", "--index-url", "https://download.pytorch.org/whl/cpu" ]) import torch import torch.nn as nn import torch.optim as optim from collections import Counter print(f"✓ PyTorch {torch.__version__} prêt") # Tokenizer simple pour remplacer Keras class SimpleTokenizer: def __init__(self): self.word_index = {} self.index_word = {} def fit_on_texts(self, texts): # Compter tous les mots word_counts = Counter() for text in texts: words = text.lower().split() word_counts.update(words) # Créer les dictionnaires word <-> index self.word_index = {word: i+1 for i, (word, _) in enumerate(word_counts.most_common())} self.index_word = {i+1: word for word, i in self.word_index.items()} def texts_to_sequences(self, texts): sequences = [] for text in texts: words = text.lower().split() sequence = [self.word_index.get(word, 0) for word in words] sequences.append(sequence) return sequences # Fonction de padding def pad_sequences(sequences, maxlen, padding='pre'): padded = [] for seq in sequences: if len(seq) > maxlen: if padding == 'pre': seq = seq[-maxlen:] else: seq = seq[:maxlen] else: if padding == 'pre': seq = [0] * (maxlen - len(seq)) + seq else: seq = seq + [0] * (maxlen - len(seq)) padded.append(seq) return np.array(padded) # Modèle PyTorch équivalent au modèle Keras class GRUWordPredictor(nn.Module): def __init__(self, vocab_size, embedding_dim=50, hidden_dim=120, max_seq_len=10): super(GRUWordPredictor, self).__init__() self.embedding = nn.Embedding(vocab_size, embedding_dim, padding_idx=0) self.gru = nn.GRU(embedding_dim, hidden_dim, batch_first=True) self.fc = nn.Linear(hidden_dim, vocab_size) self.softmax = nn.Softmax(dim=1) def forward(self, x): embedded = self.embedding(x) gru_out, _ = self.gru(embedded) # Prendre la dernière sortie de la séquence output = self.fc(gru_out[:, -1, :]) return self.softmax(output) # Variables globales model = None tokenizer = SimpleTokenizer() max_sequence_len = 0 textes = [ "la goutte d'eau qui fait déborder le vase", "Il n'y a pas de fumée sans feu", "Il faut battre le fer tant qu'il est chaud", "Il ne faut pas mettre tous ses oeufs dans le même panier", "Il faut tourner sept fois sa langue dans sa bouche avant de parler", "L'habit ne fait pas le moine", "Il ne faut pas réveiller le chat qui dort", "Il faut se méfier de l'eau qui dort", "C'est l'hôpital qui se moque de la charité", "Qui vole un oeuf vole un boeuf", "Chercher midi à quatorze heures", "Avoir un poil dans la main", "Être dans de beaux draps", "Avoir la tête dans les nuages", "Mettre les pieds dans le plat" ] def afficher_liste(liste): return "\n".join(liste) def ajouter_a_liste(liste_actuelle, nouveau_texte): if nouveau_texte: liste_actuelle.append(nouveau_texte) return liste_actuelle, "" def supprimer_de_liste(liste_actuelle, index_a_supprimer): if index_a_supprimer is not None and 0 <= index_a_supprimer < len(liste_actuelle): ligne_supprimee = liste_actuelle.pop(index_a_supprimer) message = f"Ligne supprimée : '{ligne_supprimee}'" else: message = "Aucune ligne sélectionnée pour suppression" return liste_actuelle, liste_actuelle, message def apprendre(liste_actuelle): global max_sequence_len, model, tokenizer print("Liste soumise:", liste_actuelle) # Analyse du texte tokenizer.fit_on_texts(liste_actuelle) total_words = len(tokenizer.word_index) + 1 print("nb de mots différents rencontrés :", total_words) # Afficher les premiers mots liste_mots = list(tokenizer.word_index.keys()) print("voici les premiers mots trouvés : ") for i in range(min(10, len(liste_mots))): print(f"({i+1}:'{liste_mots[i]}')", end=", ") print() # Transformation des textes en séquences input_sequences = [] for sentence in liste_actuelle: token_list = tokenizer.texts_to_sequences([sentence])[0] for i in range(1, len(token_list)): n_gram_sequence = token_list[:i+1] input_sequences.append(n_gram_sequence) # Calibrage des séquences max_sequence_len = max([len(x) for x in input_sequences]) input_sequences = pad_sequences(input_sequences, maxlen=max_sequence_len, padding='pre') # Afficher exemple de transformation if liste_actuelle: print("la phrase '", liste_actuelle[0], "' est traduite en plusieurs vecteurs :") split = liste_actuelle[0].lower().split() for i in range(min(6, len(input_sequences))): print(input_sequences[i], end=" -> '") for j in range(min(i+2, len(split))): print(split[j], end=" ") print("'") # Créer X et y X = input_sequences[:, :-1] y = input_sequences[:, -1] # Convertir en tenseurs PyTorch X_tensor = torch.LongTensor(X) y_tensor = torch.LongTensor(y) # Créer le modèle model = GRUWordPredictor( vocab_size=total_words, embedding_dim=50, hidden_dim=120, max_seq_len=max_sequence_len-1 ) # Définir la fonction de perte et l'optimiseur criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters()) # Entraînement print("patienter 30s pendant l'entrainement...") model.train() for epoch in range(3000): optimizer.zero_grad() outputs = model(X_tensor) loss = criterion(outputs, y_tensor) loss.backward() optimizer.step() if epoch % 50 == 0: print(f"Epoch {epoch}, Loss: {loss.item():.4f}") model.eval() # Mode évaluation return f"Entrainement effectué, saisissez un début de phrase pour demander la suite" def predict_next_word(start_text, nb_words): global model, tokenizer, max_sequence_len if model is None: return "Veuillez d'abord entraîner le modèle" print("===> start_text=", start_text) print(f"prédictions de {nb_words} mots.") current_text = start_text.lower() for _ in range(nb_words): token_list = tokenizer.texts_to_sequences([current_text])[0] token_list = pad_sequences([token_list], maxlen=max_sequence_len-1, padding='pre') print("===> token_list=", token_list) # Prédiction avec PyTorch with torch.no_grad(): input_tensor = torch.LongTensor(token_list) outputs = model(input_tensor) predicted = torch.argmax(outputs, dim=-1).item() print("===> predicted=", predicted) # Trouver le mot correspondant if predicted in tokenizer.index_word: word = tokenizer.index_word[predicted] current_text += " " + word else: break # Si pas de mot trouvé, arrêter print(f"Prediction: {current_text}") print("-" * 50) return current_text # Interface Gradio with gr.Blocks() as demo: # ENTETE gr.Markdown("# Exemple de prévision de mots (PyTorch)") gr.Markdown("Exemple simple, utilisant un réseau de neurone de type **GRU** avec PyTorch") gr.Markdown("- Le réseau 'apprend' les phrases de la liste (vous pouvez en ajouter)") gr.Markdown("- Cliquez sur apprendre si l'apprentissage n'a pas eu lieu") gr.Markdown("- Donnez plus bas un début de phrase et laissez le réseau en déduire la suite") # Liste de phrases with gr.Row(): sortie = gr.Textbox(label="Phrases actuelles", lines=5) liste = gr.State(textes) # Ajouter et supprimer des lignes with gr.Column(): texte = gr.Textbox(label="Nouvelle phrase courte") btn_add = gr.Button("Ajouter") # Liste déroulante pour sélectionner une ligne à supprimer choix_ligne = gr.Dropdown( choices=textes, label="Sélectionner une ligne à supprimer", type="index" ) btn_supprimer = gr.Button("Supprimer de la liste") feedback = gr.Textbox(label="Message", lines=1) # Ajouter une ligne btn_add.click( fn=ajouter_a_liste, inputs=[liste, texte], outputs=[liste, texte] ).then( fn=lambda x: "\n".join(x), inputs=[liste], outputs=[sortie] ) # Supprimer une ligne btn_supprimer.click( fn=supprimer_de_liste, inputs=[liste, choix_ligne], outputs=[liste, choix_ligne, feedback] ).then( fn=afficher_liste, inputs=[liste], outputs=[sortie] ) # Apprentissage with gr.Row(): message_apprentissage = gr.Textbox( label="Résultat de l'apprentissage", value="Apprentissage non réalisé. Cliquez et attendez > 30s" ) apprendre_btn = gr.Button("Apprendre") apprendre_btn.click( fn=apprendre, inputs=[liste], outputs=[message_apprentissage] ) # Prédiction with gr.Row(): bout_texte = gr.Textbox(label="Début de phrase ") with gr.Column(): nb_mots_pred = gr.Slider( minimum=1, maximum=6, value=3, step=1, label="Nombre de mots à prédire", interactive=True ) btn_suite = gr.Button("Poursuivre") btn_suite.click( fn=predict_next_word, inputs=[bout_texte, nb_mots_pred], outputs=[bout_texte] ) if __name__ == "__main__": demo.launch()