🤖 Blinnk-Recipe : Solution de Fusion d'Envies Alimentaires
Blinnk-Recipe est un modèle d'IA de fusion de séquences (Seq2Seq) qui transforme deux désirs ou ingrédients exprimés en langage naturel en une solution culinaire unique et pertinente. Ce modèle, développé et publié par Clemylia, est une démonstration concrète de l'application de l'IA à la créativité alimentaire.
🎯 Objectif du Modèle et Cas d'Usage
L'objectif de Blinnk-Recipe est d'agir comme un moteur de suggestion pour les combinaisons d'aliments.
- Entrée (
question) : Une phrase décrivant deux envies ou ingrédients (ex: "J'ai envie de pommes et de cannelle"). - Sortie (
reponse) : Un plat final combinant ces éléments (ex: "Tarte aux pommes a la cannelle"). - Cas d'Usage : Intégration dans des applications de recettes, des chatbots culinaires ou des générateurs d'idées de snacks.
⚙️ Architecture Technique (From Scratch)
Ce modèle est une implémentation complète de l'architecture Sequence-to-Sequence développée from scratch en PyTorch, garantissant une maîtrise totale de la chaîne de traitement.
| Composant | Description | Implémentation de Base |
|---|---|---|
| EncoderRNN | Convertit la séquence d'entrée en un vecteur de contexte. | Gated Recurrent Unit (GRU) |
| DecoderRNN | Génère la séquence de sortie mot par mot à partir de ce contexte. | Gated Recurrent Unit (GRU) |
| Tokenizer | Vocabulaire généré à partir du jeu de données pour l'encodage et le décodage. | Vocabulaire simple |
| Décodage | Utilise le Greedy Decoding avec une pénalisation des répétitions pour améliorer la cohérence. | Inférence optimisée |
📊 Jeu de Données et Entraînement
Le modèle Blinnk-Recipe a été entraîné sur un jeu de données spécifique visant à capturer une grande variété de fusions alimentaires.
- Dataset Source :
Clemylia/Solution-banane(Hugging Face) - Taille : $\approx 110$ exemples de paires question/réponse.
- Entraînement : Entraînement intensif sur un grand nombre d'époques pour compenser la taille réduite du jeu de données, assurant la convergence des poids.
👩💻 Guide d'Utilisation et de Déploiement
Le modèle est publié et prêt à être téléchargé et utilisé via la librairie huggingface_hub.
1. Installation des Dépendances
pip install torch huggingface-hub
*Exemple de codes d'inférence :
import torch
import torch.nn as nn
import json
import os
from huggingface_hub import snapshot_download
# --- 1. CONFIGURATION GLOBALE ---
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# Nom du dépôt Hugging Face pour le téléchargement
HUGGINGFACE_REPO = "Clemylia/Blinnk-Recipe"
# Définition des indices spéciaux (DOIVENT ÊTRE IDENTIQUES À L'ENTRAÎNEMENT)
SOS_token = 0 # Start Of Sequence
EOS_token = 1 # End Of Sequence
PAD_token = 2 # Padding Token
# --- 2. CLASSES DE MODÈLE (DOIVENT ÊTRE IDENTIQUES À L'ENTRAÎNEMENT) ---
class EncoderRNN(nn.Module):
"""Encodeur GRU standard."""
def __init__(self, input_size, hidden_size, embedding_dim=256):
super(EncoderRNN, self).__init__()
self.hidden_size = hidden_size
self.embedding = nn.Embedding(input_size, embedding_dim)
self.gru = nn.GRU(embedding_dim, hidden_size, batch_first=True)
def forward(self, input_seq, hidden):
embedded = self.embedding(input_seq)
output, hidden = self.gru(embedded, hidden)
return output, hidden
def initHidden(self, batch_size):
return torch.zeros(1, batch_size, self.hidden_size, device=DEVICE)
class DecoderRNN(nn.Module):
"""Décodeur GRU standard."""
def __init__(self, output_size, hidden_size, embedding_dim=256):
super(DecoderRNN, self).__init__()
self.hidden_size = hidden_size
self.embedding = nn.Embedding(output_size, embedding_dim)
self.gru = nn.GRU(embedding_dim, hidden_size, batch_first=True)
self.out = nn.Linear(hidden_size, output_size)
self.softmax = nn.LogSoftmax(dim=1)
def forward(self, input, hidden):
embedded = self.embedding(input)
output, hidden = self.gru(embedded, hidden)
output = self.out(output[:, 0, :])
output = self.softmax(output)
return output, hidden
# --- 3. CLASSE TOKENIZER ---
class LoadedTokenizer:
"""Reconstruit le tokenizer et la configuration à partir de config.json."""
def __init__(self, config_path):
with open(config_path, 'r') as f:
config = json.load(f)
# Reconversion des clés de string à int
self.index2word = {int(k): v for k, v in config['index2word'].items()}
self.word2index = config['word2index']
self.n_words = config['n_words']
self.max_length = config['max_length']
self.hidden_size = config['hidden_size']
def encode(self, sentence, add_eos=True):
"""Convertit une phrase en une liste d'indices."""
words = sentence.lower().split(' ')
# Utilise PAD_token (2) pour les mots inconnus (UNK)
indexes = [self.word2index.get(word, PAD_token) for word in words]
if add_eos:
indexes.append(EOS_token)
return indexes
def decode(self, indices):
"""Convertit une liste d'indices en une phrase."""
words = [self.index2word.get(i) for i in indices if i not in [SOS_token, EOS_token, PAD_token] and i is not None]
return ' '.join(words)
# --- 4. FONCTIONS DE CHARGEMENT ET D'INFÉRENCE ---
def load_blinnk_model_from_hf(repo_id):
"""Télécharge le modèle de Hugging Face et l'initialise."""
print(f"Téléchargement des fichiers depuis Hugging Face : {repo_id}...")
try:
# Télécharge tous les fichiers du dépôt dans le cache local
local_path = snapshot_download(repo_id=repo_id)
print(f"Fichiers téléchargés dans : {local_path}")
except Exception as e:
print(f"❌ Échec du téléchargement. Veuillez vérifier le nom du dépôt ({repo_id}) et son statut public/privé.")
raise e
# Chargement du Tokenizer et de la Config
tokenizer = LoadedTokenizer(os.path.join(local_path, "config.json"))
# Initialisation des Modèles basés sur la config
encoder = EncoderRNN(tokenizer.n_words, tokenizer.hidden_size).to(DEVICE)
decoder = DecoderRNN(tokenizer.n_words, tokenizer.hidden_size).to(DEVICE)
# Chargement des Poids
encoder.load_state_dict(torch.load(os.path.join(local_path, "encoder.pth"), map_location=DEVICE))
decoder.load_state_dict(torch.load(os.path.join(local_path, "decoder.pth"), map_location=DEVICE))
encoder.eval()
decoder.eval()
print("✅ Modèle Blinnk chargé et prêt pour l'inférence.")
return encoder, decoder, tokenizer
def evaluate(encoder, decoder, tokenizer, sentence, max_length):
"""Effectue l'inférence (génération) avec pénalisation des répétitions."""
with torch.no_grad():
input_indices = tokenizer.encode(sentence, add_eos=True)
input_tensor = torch.tensor(input_indices, dtype=torch.long, device=DEVICE).unsqueeze(0)
encoder_hidden = encoder.initHidden(1)
_, encoder_hidden = encoder(input_tensor, encoder_hidden)
decoder_input = torch.tensor([[SOS_token]], device=DEVICE)
decoder_hidden = encoder_hidden
decoded_words = []
last_ni = -1
for _ in range(max_length):
decoder_output, decoder_hidden = decoder(
decoder_input, decoder_hidden
)
# --- Pénalisation de la Répétition ---
if last_ni != -1:
decoder_output[0, last_ni] = -float('inf')
# Sélectionner le mot avec la probabilité maximale (Greedy Decoding)
topv, topi = decoder_output.data.topk(1)
ni = topi.item()
if ni == EOS_token:
break
elif ni == PAD_token:
continue
else:
decoded_words.append(tokenizer.index2word.get(ni, '<UNK>'))
last_ni = ni
decoder_input = torch.tensor([[ni]], device=DEVICE)
return ' '.join(decoded_words)
# --- 5. EXÉCUTION PRINCIPALE ---
if __name__ == "__main__":
# 1. Chargement du Modèle depuis Hugging Face
try:
encoder, decoder, tokenizer = load_blinnk_model_from_hf(HUGGINGFACE_REPO)
except Exception as e:
print(f"\n❌ Le modèle n'a pas pu être chargé. Veuillez vérifier la connexion Internet et le nom du dépôt : {e}")
exit()
# 2. Exemples de Test
print("\n--- Tests d'Inférence ---")
test_phrases = [
"j'ai envie de Banane et de bonbons",
"Je veux du chocolat et de la menthe",
"Je veux des chips et du piment",
"Je voudrais des œufs et du jambon",
"J'ai envie de fraises et de lait",
"Je veux des tacos et du poisson"
]
max_len = tokenizer.max_length
for sentence in test_phrases:
prediction = evaluate(encoder, decoder, tokenizer, sentence, max_len)
print(f"Question: '{sentence.ljust(40)}' -> Réponse Prédite: '{prediction}'")
# Testez avec votre propre envie !
my_test = input("\nEntrez votre propre envie (ex: 'je veux du poulet et du miel'): ")
if my_test:
prediction_perso = evaluate(encoder, decoder, tokenizer, my_test, max_len)
print(f"Votre envie: '{my_test}' -> Prédiction: '{prediction_perso}'")
Nombre de paramètres 1 millions
- Downloads last month
- 28
