File size: 10,561 Bytes
74d207b
32fdebd
e4f3484
4d59b66
17dc88e
32fdebd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6863bf0
32fdebd
17dc88e
32fdebd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dbed7f1
32fdebd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dbed7f1
32fdebd
 
dbed7f1
32fdebd
dbed7f1
 
 
32fdebd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
546e881
32fdebd
 
 
 
 
dbed7f1
32fdebd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dbed7f1
32fdebd
 
 
 
 
 
 
dbed7f1
32fdebd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dbed7f1
32fdebd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dbed7f1
32fdebd
dbed7f1
32fdebd
 
 
 
 
 
 
 
 
 
 
dbed7f1
 
 
 
 
 
32fdebd
 
 
 
 
 
 
 
f40785a
32fdebd
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
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()