Clemylia commited on
Commit
49187e4
·
verified ·
1 Parent(s): 618e7e7

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +307 -1
README.md CHANGED
@@ -58,4 +58,310 @@ La sortie de **Xadia-charlotte** sera une longue chaîne de mots, d'articles, et
58
  ### 🛑 Limites & Responsabilité de l'Auteur
59
 
60
  * **Cohérence Zéro :** N'attendez **aucune** cohérence syntaxique ou sémantique. Le modèle est intentionnellement sous-optimisé pour cette tâche afin de stimuler une pensée non linéaire.
61
- * **La Créativité est Manuelle :** L'utilisateur est le seul responsable de la **soudure, du rythme, des rimes et de la structure** de la chanson finale. Xadia-charlotte n'est qu'un **dictionnaire de suggestions par probabilité**.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  ### 🛑 Limites & Responsabilité de l'Auteur
59
 
60
  * **Cohérence Zéro :** N'attendez **aucune** cohérence syntaxique ou sémantique. Le modèle est intentionnellement sous-optimisé pour cette tâche afin de stimuler une pensée non linéaire.
61
+ * **La Créativité est Manuelle :** L'utilisateur est le seul responsable de la **soudure, du rythme, des rimes et de la structure** de la chanson finale. Xadia-charlotte n'est qu'un **dictionnaire de suggestions par probabilité**.
62
+
63
+ exemple de code d'utilisation :
64
+
65
+ ```
66
+ # ==============================================================================
67
+ # CHARGEMENT ET TEST DE XADIA-CHARLOTTE
68
+ # ==============================================================================
69
+
70
+ # 1. INSTALLATION DES LIBRAIRIES
71
+ # !pip install torch huggingface_hub
72
+
73
+ import torch
74
+ import torch.nn as nn
75
+ from huggingface_hub import hf_hub_download
76
+ import json
77
+ import collections
78
+ import math
79
+ import re
80
+
81
+ # --- PARAMÈTRES DE CHARGEMENT ---
82
+ # ⚠️ REMPLACEZ CETTE VALEUR PAR VOTRE ID DE MODÈLE RÉEL !
83
+ MODEL_ID = "Clemylia/Xadia-Charlotte"
84
+ TEMP_DIR = "./xadia_charlotte_download"
85
+ DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'
86
+
87
+ # ==============================================================================
88
+ # 2. REDÉFINITION DES CLASSES ORIGINALES (CRUCIAL)
89
+ # ==============================================================================
90
+
91
+ # Nous devons redéfinir les classes utilisées pour l'entraînement car elles ne sont
92
+ # pas dans la bibliothèque standard de Hugging Face.
93
+
94
+ # --- Redéfinition des Constantes Spéciales ---
95
+ UNK_TOKEN = "[UNK]"
96
+ PAD_TOKEN = "[PAD]"
97
+ SOS_TOKEN = "[SOS]"
98
+ EOS_TOKEN = "[EOS]"
99
+
100
+ # --- Redéfinition du Tokenizer Word-Level ---
101
+ class WordLevelTokenizer:
102
+ """
103
+ Tokenizer simple basé sur le niveau du mot, adapté pour le chargement.
104
+ Il chargera la carte vocabulaire directement à partir du fichier vocab.json.
105
+ """
106
+ def __init__(self, vocab_map):
107
+ # vocab_map est le dictionnaire {mot: id} chargé depuis HF
108
+ self.word_to_id = vocab_map
109
+ self.id_to_word = [None] * len(vocab_map)
110
+ for word, id_ in vocab_map.items():
111
+ self.id_to_word[id_] = word
112
+
113
+ # Récupération des IDs spéciaux
114
+ self._pad_token_id = self.word_to_id.get(PAD_TOKEN)
115
+ self._sos_token_id = self.word_to_id.get(SOS_TOKEN)
116
+ self._eos_token_id = self.word_to_id.get(EOS_TOKEN)
117
+ self._unk_token_id = self.word_to_id.get(UNK_TOKEN)
118
+
119
+ def encode(self, text):
120
+ """Convertit une phrase en liste d'IDs (utilise la même logique de nettoyage)."""
121
+ text = text.lower()
122
+ # Logique de gestion de l'apostrophe (doit correspondre à l'entraînement !)
123
+ text = re.sub(r"([cjlmnst])'", r'\1 ', text)
124
+ for punc in '.,!?:;()"-':
125
+ text = text.replace(punc, f' {punc} ')
126
+
127
+ words = text.split()
128
+
129
+ return [self.word_to_id.get(word, self._unk_token_id) for word in words]
130
+
131
+ def decode(self, token_ids):
132
+ """Convertit une liste d'IDs en phrase."""
133
+ return " ".join([self.id_to_word[id_.item()] if isinstance(id_, torch.Tensor) else self.id_to_word[id_]
134
+ for id_ in token_ids if id_ < len(self.id_to_word)])
135
+
136
+ @property
137
+ def vocab_size_final(self): return len(self.word_to_id)
138
+ @property
139
+ def pad_token_id(self): return self._pad_token_id
140
+ @property
141
+ def sos_token_id(self): return self._sos_token_id
142
+ @property
143
+ def eos_token_id(self): return self._eos_token_id
144
+
145
+
146
+ # --- Redéfinition des Blocs d'Architecture (CausalSelfAttention, XadiaBlock) ---
147
+ # (Ces classes sont complexes et n'ont pas besoin d'être modifiées si elles sont
148
+ # identiques à celles de l'entraînement.)
149
+
150
+ class CausalSelfAttention(nn.Module):
151
+ def __init__(self, d_model, num_heads, dropout):
152
+ super().__init__()
153
+ self.d_model = d_model
154
+ self.num_heads = num_heads
155
+ self.head_dim = d_model // num_heads
156
+
157
+ self.q_proj = nn.Linear(d_model, d_model)
158
+ self.k_proj = nn.Linear(d_model, d_model)
159
+ self.v_proj = nn.Linear(d_model, d_model)
160
+ self.out_proj = nn.Linear(d_model, d_model)
161
+
162
+ self.dropout = nn.Dropout(dropout)
163
+
164
+ def forward(self, x):
165
+ B, T, C = x.size()
166
+ q = self.q_proj(x).view(B, T, self.num_heads, self.head_dim).transpose(1, 2)
167
+ k = self.k_proj(x).view(B, T, self.num_heads, self.head_dim).transpose(1, 2)
168
+ v = self.v_proj(x).view(B, T, self.num_heads, self.head_dim).transpose(1, 2)
169
+
170
+ attn_scores = (q @ k.transpose(-2, -1)) / math.sqrt(self.head_dim)
171
+
172
+ causal_mask = torch.triu(torch.ones(T, T), diagonal=1).bool().to(x.device)
173
+ attn_scores = attn_scores.masked_fill(causal_mask, float('-inf'))
174
+
175
+ attn_weights = nn.functional.softmax(attn_scores, dim=-1)
176
+ attn_weights = self.dropout(attn_weights)
177
+
178
+ output = attn_weights @ v
179
+
180
+ output = output.transpose(1, 2).contiguous().view(B, T, C)
181
+ output = self.out_proj(output)
182
+
183
+ return output
184
+
185
+ class XadiaBlock(nn.Module):
186
+ def __init__(self, d_model, num_heads, ffn_factor, dropout):
187
+ super().__init__()
188
+ self.norm1 = nn.LayerNorm(d_model)
189
+ self.attn = CausalSelfAttention(d_model, num_heads, dropout)
190
+ self.norm2 = nn.LayerNorm(d_model)
191
+
192
+ self.ffn = nn.Sequential(
193
+ nn.Linear(d_model, d_model * ffn_factor),
194
+ nn.GELU(),
195
+ nn.Linear(d_model * ffn_factor, d_model),
196
+ nn.Dropout(dropout)
197
+ )
198
+ self.dropout = nn.Dropout(dropout)
199
+
200
+ def forward(self, x):
201
+ x = x + self.dropout(self.attn(self.norm1(x)))
202
+ x = x + self.dropout(self.ffn(self.norm2(x)))
203
+ return x
204
+
205
+ # --- Redéfinition de la Classe Modèle Principale ---
206
+ class XadiaCharlotteSLM(nn.Module):
207
+ def __init__(self, vocab_size, d_model, num_layers, num_heads, ffn_factor, max_seq_length, dropout):
208
+ super().__init__()
209
+ self.max_seq_length = max_seq_length
210
+
211
+ self.word_embedding = nn.Embedding(vocab_size, d_model)
212
+ self.position_embedding = nn.Embedding(max_seq_length, d_model)
213
+ self.dropout = nn.Dropout(dropout)
214
+ self.transformer_blocks = nn.ModuleList([
215
+ XadiaBlock(d_model, num_heads, ffn_factor, dropout)
216
+ for _ in range(num_layers)
217
+ ])
218
+ self.norm_final = nn.LayerNorm(d_model)
219
+ self.lm_head = nn.Linear(d_model, vocab_size, bias=False)
220
+
221
+ # Pas besoin d'init weights ici, car nous chargeons des poids entraînés
222
+
223
+ def forward(self, input_ids):
224
+ B, T = input_ids.size()
225
+
226
+ token_emb = self.word_embedding(input_ids)
227
+ position_ids = torch.arange(0, T, dtype=torch.long, device=input_ids.device)
228
+ position_emb = self.position_embedding(position_ids)
229
+
230
+ x = self.dropout(token_emb + position_emb)
231
+
232
+ for block in self.transformer_blocks:
233
+ x = block(x)
234
+
235
+ x = self.norm_final(x)
236
+ logits = self.lm_head(x)
237
+
238
+ return logits
239
+
240
+
241
+ # ==============================================================================
242
+ # 3. CHARGEMENT DU MODÈLE DEPUIS HUGGING FACE
243
+ # ==============================================================================
244
+
245
+ def load_xadia_charlotte(model_id, device):
246
+ """
247
+ Télécharge les fichiers de config, vocabulaire et poids, et instancie le modèle.
248
+ """
249
+ print(f"--- ⬇️ TÉLÉCHARGEMENT DE {model_id} ---")
250
+
251
+ # Téléchargement des fichiers essentiels
252
+ try:
253
+ # Configuration
254
+ config_path = hf_hub_download(repo_id=model_id, filename="config.json", local_dir=TEMP_DIR)
255
+ with open(config_path, 'r') as f:
256
+ config = json.load(f)
257
+ print(f"Configuration chargée: {config['vocab_size']} mots, {config['num_layers']} couches.")
258
+
259
+ # Vocabulaire
260
+ vocab_path = hf_hub_download(repo_id=model_id, filename="vocab.json", local_dir=TEMP_DIR)
261
+ with open(vocab_path, 'r', encoding='utf-8') as f:
262
+ vocab_map = json.load(f)
263
+
264
+ # Poids
265
+ weights_path = hf_hub_download(repo_id=model_id, filename="pytorch_model.bin", local_dir=TEMP_DIR)
266
+
267
+ except Exception as e:
268
+ print(f"ERREUR lors du téléchargement. Assurez-vous que l'ID de modèle est correct et que les fichiers sont présents. Détail: {e}")
269
+ return None, None
270
+
271
+ # 1. Initialisation du Tokenizer
272
+ tokenizer = WordLevelTokenizer(vocab_map)
273
+
274
+ # 2. Initialisation du Modèle
275
+ model = XadiaCharlotteSLM(
276
+ vocab_size=config['vocab_size'],
277
+ d_model=config['d_model'],
278
+ num_layers=config['num_layers'],
279
+ num_heads=config['num_heads'],
280
+ ffn_factor=config['ffn_factor'],
281
+ max_seq_length=config['max_seq_length'],
282
+ dropout=config['dropout']
283
+ ).to(device)
284
+
285
+ # 3. Chargement des poids
286
+ model.load_state_dict(torch.load(weights_path, map_location=device))
287
+ model.eval() # Mode évaluation pour l'inférence
288
+ print("Modèle Xadia-Charlotte chargé et prêt.")
289
+
290
+ return model, tokenizer
291
+
292
+ # ==============================================================================
293
+ # 4. FONCTION DE GÉNÉRATION (TEST)
294
+ # ==============================================================================
295
+
296
+ def generate_text(model, tokenizer, prompt, max_new_tokens=100, temperature=0.9):
297
+ """
298
+ Génère du texte conditionné par un prompt (refrain).
299
+ """
300
+ # 1. Préparation du prompt
301
+ prompt_ids = tokenizer.encode(prompt)
302
+ if not prompt_ids:
303
+ print("Erreur: Prompt vide ou ne contient aucun mot reconnu.")
304
+ return
305
+
306
+ input_ids = torch.tensor([prompt_ids], dtype=torch.long).to(model.lm_head.weight.device)
307
+
308
+ print(f"\n--- 🎶 TEST DE GÉNÉRATION (Temp={temperature:.2f}) ---")
309
+ print(f"Prompt (Refrain): {prompt}")
310
+ print("-" * 50)
311
+
312
+ generated_tokens = prompt_ids
313
+
314
+ with torch.no_grad():
315
+ for _ in range(max_new_tokens):
316
+ # Limiter l'input au contexte max
317
+ input_context = input_ids[:, -model.max_seq_length:]
318
+
319
+ # Forward Pass : obtenir les logits
320
+ logits = model(input_context)
321
+
322
+ # Seul le dernier token prédit nous intéresse
323
+ last_token_logits = logits[0, -1, :] / temperature
324
+
325
+ # Échantillonnage (Sampling)
326
+ probs = torch.nn.functional.softmax(last_token_logits, dim=-1)
327
+ next_token_id = torch.multinomial(probs, num_samples=1)
328
+
329
+ # Arrêt si EOS est généré
330
+ if next_token_id.item() == tokenizer.eos_token_id:
331
+ break
332
+
333
+ # Mise à jour de la séquence
334
+ input_ids = torch.cat([input_ids, next_token_id.unsqueeze(0)], dim=-1)
335
+ generated_tokens.append(next_token_id.item())
336
+
337
+ # Décodage
338
+ prompt_len = len(tokenizer.encode(prompt))
339
+ generated_couplet = tokenizer.decode(generated_tokens[prompt_len:])
340
+
341
+ print(f"Résultat Généré (Couplet): {generated_couplet}")
342
+ print("-" * 50)
343
+
344
+
345
+ # ==============================================================================
346
+ # 5. EXÉCUTION
347
+ # ==============================================================================
348
+
349
+ if __name__ == '__main__':
350
+
351
+ # ⚠️ IMPORTANT : REMPLACER PAR VOTRE ID DE MODÈLE PUBLIÉ
352
+ # Exemple : "Clemylia/Xadia-Charlotte"
353
+ YOUR_MODEL_ID = "Clemylia/Xadia-Charlotte"
354
+
355
+ # 1. Chargement du Modèle et du Tokenizer
356
+ model, tokenizer = load_xadia_charlotte(YOUR_MODEL_ID, DEVICE)
357
+
358
+ if model and tokenizer:
359
+ # 2. Test du Modèle
360
+ prompt_refrain = "quand la lumière s'éteint, mon cœur s'allume pour toi"
361
+
362
+ # Test 1: Créativité (Température élevée)
363
+ generate_text(model, tokenizer, prompt_refrain, max_new_tokens=150, temperature=0.9)
364
+
365
+ # Test 2: Cohérence (Température faible)
366
+ generate_text(model, tokenizer, prompt_refrain, max_new_tokens=150, temperature=0.5)
367
+ ```