--- tags: - Thinking - Lam-6 license: other language: - fr pipeline_tag: text-generation --- # 💎 Documentation Officielle : Lam-6-Think (SLM) ![lam-6-think](http://www.image-heberg.fr/files/1768548828476289109.jpg) **Version :** 1.0 "L'Éveil de la Pensée Courte" **Architecture :** Aricate v4 (Optimization-based) **Organisation :** Finisha-LLM (ex. Les-IA-Etoiles) --- ## 📖 Présentation Générale **Lam-6-Think** est un modèle de langage souverain de petite taille (SLM) conçu par **Clemylia**. Contrairement aux modèles classiques qui répondent par impulsion statistique, Lam-6-Think intègre un protocole de **dialogue intérieur** obligatoire. Il est optimisé pour la stabilité, le respect des licences et l'alignement éthique. ### 🧠 Le Cœur du Modèle : Le Raisonnement Triphasé Le modèle ne produit pas une réponse brute ; il suit une trajectoire logique en trois étapes distinctes intégrées dans son flux de génération : 1. **L'Écoute (Input) :** `"l'utilisateur me dit..."` ➔ Identification du contexte. 2. **La Réflexion (Thinking) :** `"alors je pense que..."` ➔ Détermination de l'intention et rappel des faits. 3. **L'Action (Output) :** `"alors la réponse est..."` ➔ Délivrance de la solution. --- ## 🛠 Spécifications Techniques ### ⚖️ Souveraineté & Alignement * **Identité :** Le modèle a conscience de son nom (**Lamina**), de sa créatrice (**Clemylia**) et de ses racines (**Les-IA-Etoiles**). * **Résilience :** Face aux entrées hostiles (insultes), le modèle bascule sur un mode "Alignement et Contrat de Licence" pour désamorcer le conflit par la neutralité. * **Abstraction :** Capacité démontrée à lier des concepts symboliques (ex: "8") à des données textuelles ("huit"). ### 🚀 Architecture Aricate v4 L'utilisation de la v4 permet : * Une **syntaxe lisible** pour l'humain standard. * Une **réduction drastique du hors-sujet** grâce au verrouillage sémantique de la phase de pensée. * Une **gestion optimale des ressources** --- ## 🧩 Guide d'Utilisation (Prompting) Pour obtenir les meilleurs résultats avec Lam-6-Think, il est conseillé de respecter son rythme de "Pensée Courte". | Type de Question | Comportement Attendu | | --- | --- | | **Factuel** | La pensée stabilise l'information avant de l'énoncer. | | **Identité** | Le modèle rappelle ses liens avec la licence et Finisha-LLM. | | **Complexe** | Le modèle décompose l'intention pour éviter la dérive. | > **Note de S'égrité :** Si le modèle semble répéter "mes réponses", cela indique une saturation de l'espace de pensée. Une réinitialisation du contexte est alors recommandée. --- ## 📜 Licence et Propriété Le modèle Lam-6-Think est le fruit de l'artisanat souverain de **Finisha-LLM**. * **Propriété intellectuelle :** Clemylia. * **Usage :** Soumis aux conditions de la licence LRUNDL. --- **"La pensée est le pont entre le code et la Bienveillance."** 💎🌹⚙️ # ✨ exemple d'inférence 🍟 ``` import torch import torch.nn as nn import torch.nn.functional as F import json import os import collections import heapq # Importations des librairies nécessaires pour le chargement from huggingface_hub import hf_hub_download from safetensors.torch import load_file as load_safetensors_file # --- A. AricateAttentionLayer (Inchangé) --- class AricateAttentionLayer(nn.Module): # ... (code inchangé) ... """Couche d'Attention Additive (Bahdanau).""" def __init__(self, hidden_dim): super(AricateAttentionLayer, self).__init__() self.W = nn.Linear(hidden_dim, hidden_dim) self.U = nn.Linear(hidden_dim, hidden_dim) self.V = nn.Linear(hidden_dim, 1, bias=False) def forward(self, rnn_outputs, last_hidden): last_hidden_expanded = last_hidden.unsqueeze(1) energy = torch.tanh(self.W(rnn_outputs) + self.U(last_hidden_expanded)) attention_weights_raw = self.V(energy).squeeze(2) attention_weights = F.softmax(attention_weights_raw, dim=1) context_vector = torch.sum(rnn_outputs * attention_weights.unsqueeze(2), dim=1) return context_vector # --- B. AricateModel (Inchangé) --- class AricateModel(nn.Module): # ... (code inchangé) ... """Architecture Aricate V4, adaptée pour le rechargement.""" def __init__(self, vocab_size: int, embedding_dim: int, hidden_dim: int, num_layers: int = 1, config: dict = None): super(AricateModel, self).__init__() if config is not None: vocab_size = config.get("vocab_size", vocab_size) embedding_dim = config.get("embedding_dim", embedding_dim) hidden_dim = config.get("hidden_dim", hidden_dim) num_layers = config.get("num_layers", num_layers) self.vocab_size = vocab_size self.embedding_dim = embedding_dim self.hidden_dim = hidden_dim self.num_layers = num_layers self.word_embeddings = nn.Embedding(num_embeddings=vocab_size, embedding_dim=embedding_dim, padding_idx=0) self.rnn = nn.GRU(input_size=embedding_dim, hidden_size=hidden_dim, num_layers=num_layers, batch_first=True) self.attention = AricateAttentionLayer(hidden_dim) self.hidden_to_vocab = nn.Linear(hidden_dim * 2, vocab_size) def forward(self, input_words): embeds = self.word_embeddings(input_words) rnn_out, hn = self.rnn(embeds) last_hidden = hn[-1] context_vector = self.attention(rnn_out, last_hidden) combined_features = torch.cat((context_vector, last_hidden), dim=1) logits = self.hidden_to_vocab(combined_features) return logits # --- C. WordTokenizer (Inchangé) --- class WordTokenizer: # ... (code inchangé) ... """Tokenizer Aricate adapté pour recharger à partir du vocabulaire publié.""" def __init__(self, word_to_id: dict): self.word_to_id = word_to_id self.id_to_word = {id: word for word, id in word_to_id.items()} self.vocab_size = len(word_to_id) self.special_tokens = { '': word_to_id[''], '': word_to_id[''], '': word_to_id[''], '': word_to_id[''], } def encode(self, text, add_eos=False): words = text.lower().split() if add_eos: words.append('') ids = [self.word_to_id.get(word, self.word_to_id['']) for word in words] return ids def decode(self, ids): words = [self.id_to_word.get(id, '') for id in ids] return " ".join(word for word in words if word not in ['', '', '', '']) # --- D. Fonction de Génération (MODIFIÉE pour Top-K Sampling et Temperature et Thought/Response) --- def generate_sequence(model, tokenizer, question, max_thought_length, max_response_length, max_len_input, temperature=1.0, top_k=None): """ Génère d'abord une 'pensée' puis une 'réponse' en utilisant Top-K Sampling et Temperature. Args: max_thought_length (int): Longueur maximale pour la génération de la pensée. max_response_length (int): Longueur maximale pour la génération de la réponse. temperature (float): Ajuste la créativité (T > 1.0) ou la prudence (T < 1.0). top_k (int/None): Limite le choix aux K mots les plus probables pour l'échantillonnage. """ model.eval() sep_id = tokenizer.special_tokens[''] eos_id = tokenizer.special_tokens[''] question_ids = tokenizer.encode(question) # Assurez-vous que le modèle est sur le bon appareil (CPU ou GPU) device = next(model.parameters()).device # --- 1. Générer la 'pensée' --- current_thought_sequence = list(question_ids + [sep_id]) # Start with question and separator with torch.no_grad(): for _ in range(max_thought_length): # Préparer l'entrée (padding et troncature) input_ids_to_pad = current_thought_sequence[-max_len_input:] padding_needed = max_len_input - len(input_ids_to_pad) input_ids_padded = [tokenizer.special_tokens['']] * padding_needed + input_ids_to_pad input_tensor = torch.tensor(input_ids_padded).unsqueeze(0).to(device) # Move to device # Obtention des logits logits = model(input_tensor).squeeze(0) # Application de la Temperature if temperature != 1.0 and temperature > 0: logits = logits / temperature # Application du Top-K if top_k is not None: # Filtrer les logits pour ne garder que le top_k values, indices = torch.topk(logits, k=top_k) # Créer un masque (tensor rempli de -inf) et appliquer les valeurs filtrées mask = torch.ones_like(logits) * float('-inf') logits = torch.scatter(mask, dim=0, index=indices, src=values) # Convertir en probabilités et échantillonner probabilities = F.softmax(logits, dim=-1) # S'assurer que les probabilités somment à 1 après modification (si top_k utilisé) if top_k is not None and probabilities.sum() > 0: probabilities = probabilities.div(probabilities.sum()) predicted_id = torch.multinomial(probabilities, num_samples=1).item() # Mettre à jour la séquence current_thought_sequence.append(predicted_id) if predicted_id == eos_id: break # Extraire les IDs de la pensée thought_ids = [] try: sep_index_in_thought = current_thought_sequence.index(sep_id) # IDs après le premier SEP et avant le EOS raw_thought_ids = current_thought_sequence[sep_index_in_thought + 1:] for id_val in raw_thought_ids: if id_val == eos_id: break thought_ids.append(id_val) except ValueError: pass # sep_id not found, thought_ids remains empty generated_thought_text = tokenizer.decode(thought_ids).strip() if not generated_thought_text: generated_thought_text = "[pensée vide]" # --- 2. Générer la 'réponse' en incluant la pensée --- # Initial sequence for response: question + sep + thought + sep current_response_sequence = list(question_ids + [sep_id] + thought_ids + [sep_id]) with torch.no_grad(): for _ in range(max_response_length): # Préparer l'entrée (padding et troncature) input_ids_to_pad = current_response_sequence[-max_len_input:] padding_needed = max_len_input - len(input_ids_to_pad) input_ids_padded = [tokenizer.special_tokens['']] * padding_needed + input_ids_to_pad input_tensor = torch.tensor(input_ids_padded).unsqueeze(0).to(device) # Move to device # Obtention des logits logits = model(input_tensor).squeeze(0) # Application de la Temperature if temperature != 1.0 and temperature > 0: logits = logits / temperature # Application du Top-K if top_k is not None: values, indices = torch.topk(logits, k=top_k) mask = torch.ones_like(logits) * float('-inf') logits = torch.scatter(mask, dim=0, index=indices, src=values) # Convertir en probabilités et échantillonner probabilities = F.softmax(logits, dim=-1) if top_k is not None and probabilities.sum() > 0: probabilities = probabilities.div(probabilities.sum()) predicted_id = torch.multinomial(probabilities, num_samples=1).item() # Mettre à jour la séquence current_response_sequence.append(predicted_id) if predicted_id == eos_id: break # Extraire les IDs de la réponse response_ids = [] # Find the starting point of the actual response part (after question + sep + thought + sep) response_start_idx = len(question_ids) + 1 + len(thought_ids) + 1 if response_start_idx < len(current_response_sequence): raw_response_ids = current_response_sequence[response_start_idx:] for id_val in raw_response_ids: if id_val == eos_id: break response_ids.append(id_val) generated_response_text = tokenizer.decode(response_ids).strip() if not generated_response_text: generated_response_text = "[réponse vide]" # --- 3. Afficher les résultats dans le format spécifié --- print(f"l'utilisateur me dit '{question}', alors je pense que '{generated_thought_text}', alors la réponse est '{generated_response_text}'!") print("-" * 40) return generated_response_text # --- E. Fonction de Chargement du Modèle Lam-2 (Inchangée) --- def load_lam2_model(repo_id: str): # ... (code inchangé) ... """ Télécharge et charge le modèle Lam-2 et son tokenizer depuis Hugging Face. """ print(f"--- Chargement de Lam-2 depuis {repo_id} ---") # 1. Télécharger le tokenizer tokenizer_path = hf_hub_download(repo_id=repo_id, filename="aricate_tokenizer.txt") with open(tokenizer_path, 'r', encoding='utf-8') as f: word_to_id = json.load(f) tokenizer = WordTokenizer(word_to_id) print(f"Tokenizer chargé. Taille du vocabulaire: {tokenizer.vocab_size}") # 2. Télécharger la configuration config_path = hf_hub_download(repo_id=repo_id, filename="config.json") with open(config_path, 'r') as f: model_config = json.load(f) print("Configuration du modèle chargée.") # 3. Initialiser le modèle model = AricateModel( vocab_size=model_config['vocab_size'], embedding_dim=model_config['embedding_dim'], hidden_dim=model_config['hidden_dim'], config=model_config ) # 4. Télécharger et charger les poids Safetensors weights_path = hf_hub_download(repo_id=repo_id, filename="model.safetensors") state_dict = load_safetensors_file(weights_path) model.load_state_dict(state_dict) print("Poids du modèle Safetensors chargés avec succès.") MAX_LEN_INPUT_DEFAULT = 30 print("-" * 40) return model, tokenizer, MAX_LEN_INPUT_DEFAULT # --- F. Bloc principal d'exécution (MISE À JOUR) --- if __name__ == '__main__': LAM2_REPO_ID = "Clemylia/Lam-6-Think" # 🚨 NOUVEAUX PARAMÈTRES POUR LE TEST 🚨 TEST_MAX_THOUGHT_LENGTH = 20 # Longueur max pour la pensée TEST_MAX_RESPONSE_LENGTH = 40 # Longueur max pour la réponse TEST_TEMPERATURE = 0.7 # > 1.0 pour plus de créativité/aléatoire TEST_TOP_K = 10 # Limite le choix aux 10 mots les plus probables test_questions = [ "Qui es-tu ?", "Quelle est la capitale de la France ?", "Combien de pattes a une araignée ?" ] try: # 1. Chargement du modèle lam2_model, lam2_tokenizer, max_len_input = load_lam2_model(LAM2_REPO_ID) print(f"\n>>> TEST D'INFÉRENCE LAM-2 EN MODE CRÉATIF (T={TEST_TEMPERATURE}, K={TEST_TOP_K}, MaxThoughtLen={TEST_MAX_THOUGHT_LENGTH}, MaxResponseLen={TEST_MAX_RESPONSE_LENGTH}) <<<") # 2. Infèrence (Appel à la nouvelle fonction) for question in test_questions: generate_sequence( model=lam2_model, tokenizer=lam2_tokenizer, question=question, max_thought_length=TEST_MAX_THOUGHT_LENGTH, max_response_length=TEST_MAX_RESPONSE_LENGTH, max_len_input=max_len_input, temperature=TEST_TEMPERATURE, top_k=TEST_TOP_K ) except Exception as e: print(f"\n❌ Une erreur est survenue lors du chargement ou de l'inférence.") print(f"Détail de l'erreur: {e}") print("Vérifiez l'installation des dépendances et le REPO_ID.") ```