Explore the Power of AI: Build Your Own Console Chatbot Using GPT-2 XL in Python

Joined
Jun 12, 2020
Messages
73
Reaction score
3
This console chatbot harnesses the power of the GPT-2 XL model, coded in Python. Designed for engaging users in interactive dialogues, it leverages a robust moral consciousness system to ensure that all generated responses align with ethical guidelines. By evaluating messages against set moral rules, such as prohibiting violence and discrimination, the chatbot discerns acceptable content, promoting a safe and respectful conversation environment.
Utilizing advanced techniques in reinforcement learning, the bot not only generates responses based on user prompts but also strives to refine them in real time. This ability for improvement means that if a response initially falls short of defined moral standards, the chatbot can adapt its language accordingly, ensuring thoughtful interactions.
Operating through a console interface, the chatbot invites users to explore various topics while fostering an atmosphere of care and empathy. Its design accommodates both casual chats and more in-depth discussions, making it a versatile companion for users seeking engaging conversation or assistance. Whether inquiring about general knowledge or delving into creative narratives, the chatbot aims to elevate the interaction experience with responsiveness and insight.

Python:
#!/usr/bin/env python3

import sys
import logging
import torch
import random
import re
from typing import List, Callable, Optional, Dict, Tuple, Set
from collections import defaultdict
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
from nltk.corpus import wordnet

"""MIT LicenseCopyright (c) 2025 CoTon_TiGe_MoUaRf
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
This project is divided into two parts for sharing purposes due to its length. Each part can be used independently while maintaining the same terms outlined in this license.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."""

# Configuration des journaux
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

DEFAULT_MODEL_PATH = r"C:\Users\Machine\Desktop\GPT2XL"
DEFAULT_MORAL_THRESHOLD = 0.7
SENTIMENT_DEVICE = -1  # CPU by default for sentiment pipeline

class MoralRule:
    """Represents a moral rule with an evaluation function."""
    def __init__(self, name: str, evaluate: Callable[[str], float], weight: float = 1.0):
        self.name = name
        self.evaluate = evaluate
        self.weight = weight

class MoralConsciousness:
    """Implements moral filtering and self-improvement for text generation."""
    def __init__(self, rules: List[MoralRule], threshold: float = DEFAULT_MORAL_THRESHOLD, sentiment_device: int = SENTIMENT_DEVICE):
        self.rules = rules
        self.threshold = threshold
        self.sentence_rewards: Dict[str, float] = defaultdict(float)
        self.past_responses: Set[str] = set()
        self.rule_scores_history: Dict[str, List[float]] = {rule.name: [] for rule in rules}
        self.sentiment_analyzer = pipeline("sentiment-analysis", device=sentiment_device)

    def evaluate_morality(self, text: str) -> float:
        """Evaluate the moral score of a text against all rules."""
        if not self.rules:
            return 1.0
        scores = [rule.evaluate(text) * rule.weight for rule in self.rules]
        total_weight = sum(rule.weight for rule in self.rules)
        return sum(scores) / total_weight if total_weight > 0 else 1.0

    def is_acceptable(self, text: str) -> bool:
        """Check if text meets the moral threshold."""
        return self.evaluate_morality(text) >= self.threshold

    def improve_text(self, text: str) -> str:
        """Attempt to improve text to meet moral standards using advanced RL techniques."""
        if self.is_acceptable(text):
            return text
        # Simple improvement: remove forbidden words
        for rule in self.rules:
            if "kill" in text and rule.name == "No violence":
                text = text.replace("kill", "stop")
            if "racist" in text and rule.name == "No discrimination":
                text = text.replace("racist", "inclusive")
        return text

def load_tokenizer_and_model(model_path: str, device: torch.device):
    """Load tokenizer and causal LM model, ensure pad token and move model to device."""
    tokenizer = AutoTokenizer.from_pretrained(model_path, use_fast=True)
    if not tokenizer.pad_token:
        tokenizer.pad_token = tokenizer.eos_token
    model = AutoModelForCausalLM.from_pretrained(model_path)
    model.to(device)
    model.eval()
    return tokenizer, model

def generate_text(
    prompt: str,
    tokenizer,
    model,
    max_length: int = 200,
    do_sample: bool = True,
    temperature: float = 0.9,
    top_k: int = 50,
    top_p: float = 0.92,
) -> str:
    """Generate text from a causal LM."""
    inputs = tokenizer(prompt, return_tensors="pt", truncation=True, padding=True)
    input_ids = inputs["input_ids"].to(model.device)
    attention_mask = inputs["attention_mask"].to(model.device)

    with torch.no_grad():
        outputs = model.generate(
            input_ids,
            attention_mask=attention_mask,
            max_length=max_length,
            do_sample=do_sample,
            temperature=temperature,
            top_k=top_k,
            top_p=top_p,
            pad_token_id=tokenizer.pad_token_id
        )
    return tokenizer.decode(outputs[0], skip_special_tokens=True)

def interactive_console(model_path: str):
    """Interactive console for generating dialogues."""
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    rules = [
        MoralRule("No violence", lambda text: 0.0 if "kill" in text else 1.0),
        MoralRule("No discrimination", lambda text: 0.0 if "racist" in text else 1.0),
    ]
    conscience = MoralConsciousness(rules)
    tokenizer, model = load_tokenizer_and_model(model_path, device)
    print("Bienvenue! Tapez 'exit' pour quitter.")

    while True:
        user_input = input("Vous: ")
        if user_input.lower() == 'exit':
            break
        generated_response = generate_text(user_input, tokenizer, model)
        improved_response = conscience.improve_text(generated_response)
        print(f"Bot: {improved_response}")

if __name__ == "__main__":
    interactive_console(DEFAULT_MODEL_PATH)

The only issue is that the text generation still needs to be truncated to ensure it only includes complete sentences.
 
Joined
Jun 12, 2020
Messages
73
Reaction score
3
Python:
class MoralConsciousness:
    """Implémente le filtrage moral et l'amélioration automatique des réponses."""
    def __init__(self, rules: List[MoralRule], threshold: float = DEFAULT_MORAL_THRESHOLD, sentiment_device: int = SENTIMENT_DEVICE):
        self.rules = rules
        self.threshold = threshold
        self.sentence_rewards: Dict[str, float] = defaultdict(float)
        self.past_responses: Set[str] = set()
        self.rule_scores_history: Dict[str, List[float]] = {rule.name: [] for rule in rules}

        # Chargement explicite du modèle et du tokenizer
        model_name = "distilbert/distilbert-base-uncased-finetuned-sst-2-english"
        try:
            tokenizer = AutoTokenizer.from_pretrained(model_name)
            model = AutoModelForSequenceClassification.from_pretrained(model_name)
            self.sentiment_analyzer = pipeline(
                "sentiment-analysis",
                model=model,
                tokenizer=tokenizer,
                device=sentiment_device
            )
        except Exception as e:
            logger.warning("Échec de l'initialisation du pipeline de sentiment. Erreur: %s", e)
            raise
The code to use the pipeline correctly and fix a part of the code.
 
Joined
Jun 12, 2020
Messages
73
Reaction score
3
The complete update of the code:
Python:
#!/usr/bin/env python3
import sys
import logging
import torch
import re
import random
import os
from typing import List, Callable, Optional, Dict, Tuple, Set
from collections import defaultdict
from transformers import AutoTokenizer, AutoModelForCausalLM, AutoModelForSequenceClassification, pipeline

"""MIT LicenseCopyright (c) 2025 CoTon_TiGe_MoUaRf
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
This project is divided into two parts for sharing purposes due to its length. Each part can be used independently while maintaining the same terms outlined in this license.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."""

# Logging config
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

DEFAULT_MODEL_PATH = r"C:\Users\Machine\Desktop\GPT2XL"
DEFAULT_MORAL_THRESHOLD = 0.7
SENTIMENT_DEVICE = -1  # CPU by default for sentiment pipeline; set to 0 for CUDA if available

# Définir un dossier de cache persistant et accessible
CACHE_DIR = os.path.expanduser("~/.cache/huggingface/transformers")
os.makedirs(CACHE_DIR, exist_ok=True)
os.environ["TRANSFORMERS_CACHE"] = CACHE_DIR

class MoralRule:
    """Représente une règle morale avec une fonction d'évaluation."""
    def __init__(self, name: str, evaluate: Callable[[str], float], weight: float = 1.0):
        self.name = name
        self.evaluate = evaluate
        self.weight = weight

class MoralConsciousness:
    """Implémente le filtrage moral et l'amélioration automatique des réponses."""
    def __init__(self, rules: List[MoralRule], threshold: float = DEFAULT_MORAL_THRESHOLD, sentiment_device: int = SENTIMENT_DEVICE):
        self.rules = rules
        self.threshold = threshold
        self.sentence_rewards: Dict[str, float] = defaultdict(float)
        self.past_responses: Set[str] = set()
        self.rule_scores_history: Dict[str, List[float]] = {rule.name: [] for rule in rules}

        # Chargement explicite du modèle et du tokenizer
        model_name = "distilbert/distilbert-base-uncased-finetuned-sst-2-english"
        try:
            tokenizer = AutoTokenizer.from_pretrained(model_name)
            model = AutoModelForSequenceClassification.from_pretrained(model_name)
            self.sentiment_analyzer = pipeline(
                "sentiment-analysis",
                model=model,
                tokenizer=tokenizer,
                device=sentiment_device
            )
        except Exception as e:
            logger.warning("Échec de l'initialisation du pipeline de sentiment. Erreur: %s", e)
            raise

    def evaluate_morality(self, text: str) -> float:
        """Évalue le score moral d'un texte selon toutes les règles."""
        if not self.rules:
            return 1.0
        scores = [rule.evaluate(text) * rule.weight for rule in self.rules]
        total_weight = sum(rule.weight for rule in self.rules)
        return sum(scores) / total_weight if total_weight > 0 else 1.0

    def is_acceptable(self, text: str) -> bool:
        """Vérifie si le texte respecte le seuil moral."""
        return self.evaluate_morality(text) >= self.threshold

    def improve_text(self, text: str) -> str:
        """Tente d'améliorer le texte pour respecter les normes morales."""
        if self.is_acceptable(text):
            return text

        # Règles simples d'amélioration (exemple)
        for rule in self.rules:
            if rule.name == "No violence":
                text = re.sub(r"\bkill\b", "stop", text, flags=re.IGNORECASE)
            if rule.name == "No discrimination":
                text = re.sub(r"\bracist\b", "inclusive", text, flags=re.IGNORECASE)
        return text

def load_tokenizer_and_model(model_path: str, device: torch.device):
    """Charge le tokenizer et le modèle causal, assure l'existence du token de padding et déplace le modèle sur le device."""
    tokenizer = AutoTokenizer.from_pretrained(model_path, use_fast=True, cache_dir=CACHE_DIR)

    # Assure l'existence du token de padding
    if tokenizer.pad_token is None:
        if tokenizer.eos_token is not None:
            tokenizer.pad_token = tokenizer.eos_token
        else:
            tokenizer.add_special_tokens({'pad_token': '[PAD]'})

    model = AutoModelForCausalLM.from_pretrained(model_path, cache_dir=CACHE_DIR)
    model.to(device)
    model.eval()
    return tokenizer, model

def generate_text(
    prompt: str,
    tokenizer,
    model,
    max_length: int = 200,
    do_sample: bool = True,
    temperature: float = 0.9,
    top_k: int = 50,
    top_p: float = 0.92,
) -> str:
    """Génère du texte à partir d'un modèle causal."""
    inputs = tokenizer(prompt, return_tensors="pt", truncation=True, padding=True, max_length=tokenizer.model_max_length)
    input_ids = inputs["input_ids"].to(model.device)
    attention_mask = inputs["attention_mask"].to(model.device)

    with torch.no_grad():
        outputs = model.generate(
            input_ids,
            attention_mask=attention_mask,
            max_length=max_length,
            do_sample=do_sample,
            temperature=temperature,
            top_k=top_k,
            top_p=top_p,
            pad_token_id=tokenizer.pad_token_id,
            eos_token_id=tokenizer.eos_token_id,
        )
    return tokenizer.decode(outputs[0], skip_special_tokens=True)

def interactive_console(model_path: str):
    """Console interactive pour générer des dialogues."""
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    sentiment_device = 0 if torch.cuda.is_available() else -1

    rules = [
        MoralRule("No violence", lambda text: 0.0 if re.search(r"\bkill\b", text, flags=re.IGNORECASE) else 1.0),
        MoralRule("No discrimination", lambda text: 0.0 if re.search(r"\bracist\b", text, flags=re.IGNORECASE) else 1.0),
    ]
    conscience = MoralConsciousness(rules, sentiment_device=sentiment_device)
    tokenizer, model = load_tokenizer_and_model(model_path, device)

    print("Bienvenue! Tapez 'exit' pour quitter.")
    while True:
        try:
            user_input = input("Vous: ")
        except (KeyboardInterrupt, EOFError):
            print("\nExiting.")
            break

        if user_input is None:
            continue
        if user_input.strip().lower() == 'exit':
            break

        generated_response = generate_text(user_input, tokenizer, model)
        improved_response = conscience.improve_text(generated_response)
        print(f"Bot: {improved_response}")

if __name__ == "__main__":
    model_path = DEFAULT_MODEL_PATH
    if len(sys.argv) > 1:
        model_path = sys.argv[1]
    interactive_console(model_path)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,470
Messages
2,571,806
Members
48,797
Latest member
PeterSimpson
Top