💎 Documentation Officielle : Lam-6-Think (SLM)
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 :
- L'Écoute (Input) :
"l'utilisateur me dit..."➔ Identification du contexte. - La Réflexion (Thinking) :
"alors je pense que..."➔ Détermination de l'intention et rappel des faits. - 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 = {
'<pad>': word_to_id['<pad>'],
'<unk>': word_to_id['<unk>'],
'<eos>': word_to_id['<eos>'],
'<sep>': word_to_id['<sep>'],
}
def encode(self, text, add_eos=False):
words = text.lower().split()
if add_eos:
words.append('<eos>')
ids = [self.word_to_id.get(word, self.word_to_id['<unk>']) for word in words]
return ids
def decode(self, ids):
words = [self.id_to_word.get(id, '<unk>') for id in ids]
return " ".join(word for word in words if word not in ['<pad>', '<unk>', '<eos>', '<sep>'])
# --- 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['<sep>']
eos_id = tokenizer.special_tokens['<eos>']
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['<pad>']] * 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['<pad>']] * 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.")
- Downloads last month
- 56
