Spaces:
Sleeping
Sleeping
import warnings | |
import numpy as np | |
import pandas as pd | |
import os | |
import json | |
import random | |
import gradio as gr | |
import torch | |
import torch.nn as nn | |
import torch.optim as optim | |
from torch.utils.data import DataLoader, Dataset | |
from sklearn.ensemble import RandomForestClassifier | |
from sklearn.model_selection import train_test_split | |
from sklearn.preprocessing import OneHotEncoder | |
from deap import base, creator, tools, algorithms | |
from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline, AutoModelForSequenceClassification | |
import gc | |
warnings.filterwarnings('ignore', category=FutureWarning, module='huggingface_hub.file_download') | |
# Initialize Example Emotions Dataset | |
data = { | |
'context': [ | |
'I am happy', 'I am sad', 'I am angry', 'I am excited', 'I am calm', | |
'I am feeling joyful', 'I am grieving', 'I am feeling peaceful', 'I am frustrated', | |
'I am determined', 'I feel resentment', 'I am feeling glorious', 'I am motivated', | |
'I am surprised', 'I am fearful', 'I am trusting', 'I feel disgust', 'I am optimistic', | |
'I am pessimistic', 'I feel bored', 'I am envious' | |
], | |
'emotion': [ | |
'joy', 'sadness', 'anger', 'joy', 'calmness', 'joy', 'grief', 'calmness', 'anger', | |
'determination', 'resentment', 'glory', 'motivation', 'surprise', 'fear', 'trust', | |
'disgust', 'optimism', 'pessimism', 'boredom', 'envy' | |
] | |
} | |
df = pd.DataFrame(data) | |
# Encoding the contexts using One-Hot Encoding (memory-efficient) | |
encoder = OneHotEncoder(handle_unknown='ignore', sparse=True) | |
contexts_encoded = encoder.fit_transform(df[['context']]) | |
# Encoding emotions | |
emotions_target = pd.Categorical(df['emotion']).codes | |
emotion_classes = pd.Categorical(df['emotion']).categories | |
# Load pre-trained BERT model for emotion prediction | |
emotion_prediction_model = AutoModelForSequenceClassification.from_pretrained("bhadresh-savani/distilbert-base-uncased-emotion") | |
emotion_prediction_tokenizer = AutoTokenizer.from_pretrained("bhadresh-savani/distilbert-base-uncased-emotion") | |
# Lazy loading for the fine-tuned language model | |
_finetuned_lm_tokenizer = None | |
_finetuned_lm_model = None | |
def get_finetuned_lm_model(): | |
global _finetuned_lm_tokenizer, _finetuned_lm_model | |
if _finetuned_lm_tokenizer is None or _finetuned_lm_model is None: | |
finetuned_lm_model_name = "microsoft/DialoGPT-large" # Replace with your fine-tuned language model name | |
_finetuned_lm_tokenizer = AutoTokenizer.from_pretrained(finetuned_lm_model_name) | |
_finetuned_lm_model = AutoModelForCausalLM.from_pretrained(finetuned_lm_model_name, device_map="auto", low_cpu_mem_usage=True) | |
return _finetuned_lm_tokenizer, _finetuned_lm_model | |
# Enhanced Emotional States | |
emotions = { | |
'joy': {'percentage': 10, 'motivation': 'positive', 'intensity': 0}, | |
'pleasure': {'percentage': 10, 'motivation': 'selfish', 'intensity': 0}, | |
'sadness': {'percentage': 10, 'motivation': 'negative', 'intensity': 0}, | |
'grief': {'percentage': 10, 'motivation': 'negative', 'intensity': 0}, | |
'anger': {'percentage': 10, 'motivation': 'traumatic or strong', 'intensity': 0}, | |
'calmness': {'percentage': 10, 'motivation': 'neutral', 'intensity': 0}, | |
'determination': {'percentage': 10, 'motivation': 'positive', 'intensity': 0}, | |
'resentment': {'percentage': 10, 'motivation': 'negative', 'intensity': 0}, | |
'glory': {'percentage': 10, 'motivation': 'positive', 'intensity': 0}, | |
'motivation': {'percentage': 'motivation': {'percentage': 10, 'motivation': 'positive', 'intensity': 0}, | |
'ideal_state': {'percentage': 100, 'motivation': 'balanced', 'intensity': 0}, | |
'fear': {'percentage': 10, 'motivation': 'defensive', 'intensity': 0}, | |
'surprise': {'percentage': 10, 'motivation': 'unexpected', 'intensity': 0}, | |
'anticipation': {'percentage': 10, 'motivation': 'predictive', 'intensity': 0}, | |
'trust': {'percentage': 10, 'motivation': 'reliable', 'intensity': 0}, | |
'disgust': {'percentage': 10, 'motivation': 'repulsive', 'intensity': 0}, | |
'optimism': {'percentage': 10, 'motivation': 'hopeful', 'intensity': 0}, | |
'pessimism': {'percentage': 10, 'motivation': 'doubtful', 'intensity': 0}, | |
'boredom': {'percentage': 10, 'motivation': 'indifferent', 'intensity': 0}, | |
'envy': {'percentage': 10, 'motivation': 'jealous', 'intensity': 0} | |
} | |
total_percentage = 200 | |
emotion_history_file = 'emotion_history.json' | |
def load_historical_data(file_path=emotion_history_file): | |
if os.path.exists(file_path): | |
with open(file_path, 'r') as file: | |
return json.load(file) | |
return [] | |
def save_historical_data(historical_data, file_path=emotion_history_file): | |
with open(file_path, 'w') as file: | |
json.dump(historical_data, file) | |
emotion_history = load_historical_data() | |
def update_emotion(emotion, percentage, intensity): | |
""" | |
Updates the emotional state based on the provided emotion, percentage, and intensity. | |
Args: | |
emotion (str): Name of the emotion to update. | |
percentage (float): Percentage change to apply to the emotion. | |
intensity (float): Intensity value to update for the emotion. | |
""" | |
emotions['ideal_state']['percentage'] -= percentage | |
emotions[emotion]['percentage'] += percentage | |
emotions[emotion]['intensity'] = intensity | |
total_current = sum(e['percentage'] for e in emotions.values()) | |
adjustment = total_percentage - total_current | |
emotions['ideal_state']['percentage'] += adjustment | |
def normalize_context(context): | |
""" | |
Normalizes the context text by converting it to lowercase and removing whitespace. | |
Args: | |
context (str): The context text to normalize. | |
Returns: | |
str: The normalized context text. | |
""" | |
return context.lower().strip() | |
# Improved Genetic Algorithm for Emotion Evolution with clear function definition | |
def evolve_emotions(): | |
""" | |
Evolves the emotional states using a genetic algorithm to find an optimal balance. | |
This function utilizes the DEAP library to implement a genetic algorithm. It aims to | |
find a combination of emotional states that minimizes the following criteria: | |
1. Difference between the ideal state (100%) and its actual percentage. | |
2. Sum of all non-ideal emotional percentages. | |
3. Range of emotional intensities. | |
The weights for these criteria can be adjusted in the `creator.create("FitnessMulti", ...)` line. | |
""" | |
creator.create("FitnessMulti", base.Fitness, weights=(-1.0, -0.5, -0.2)) # Weights for evaluation criteria | |
creator.create("Individual", list, fitness=creator.FitnessMulti) | |
toolbox = base.Toolbox() | |
toolbox.register("attr_float", random.uniform, 0, 20) | |
toolbox.register("attr_intensity", random.uniform, 0, 10) | |
toolbox.register("individual", tools.initCycle, creator.Individual, | |
(toolbox.attr_float,) * (len(emotions) - 1) + # Individual emotions | |
(toolbox.attr_intensity,) * len(emotions) + # Intensities | |
(lambda: 100,), n=1) | |
toolbox.register("population", tools.initRepeat, list, toolbox.individual) | |
toolbox.register("mate", tools.cxTwoPoint) | |
toolbox.register("mutate", tools.mutGaussian, mu=0, sigma=1, indpb=0.2 | |
toolbox.register("select", tools.selNSGA2) | |
toolbox.register("evaluate", evaluate) | |
population = toolbox.population(n=100) | |
algorithms.eaMuPlusLambda(population, toolbox, mu=50, lambda_=100, cxpb=0.7, mutpb=0.2, ngen=100, | |
stats=None, halloffame=None, verbose=False) | |
best_individual = tools.selBest(population, k=1)[0] | |
emotion_values = best_individual[:len(emotions) - 1] | |
intensities = best_individual[-21:-1] | |
ideal_state = best_individual[-1] | |
for i, emotion in enumerate(emotions): | |
emotions[emotion]['percentage'] = emotion_values[i] | |
emotions[emotion]['intensity'] = intensities[i] | |
emotions['ideal_state']['percentage'] = ideal_state | |
def predict_emotion(context): | |
""" | |
Predicts the emotion from the provided context using a pre-trained BERT model. | |
Args: | |
context (str): The context text for emotion prediction. | |
Returns: | |
str: The predicted emotion. | |
""" | |
emotion_prediction_pipeline = pipeline('text-classification', model=emotion_prediction_model, tokenizer=emotion_prediction_tokenizer, top_k=None) | |
predictions = emotion_prediction_pipeline(context) | |
emotion_scores = {prediction['label']: prediction['score'] for prediction in predictions[0]} | |
emotion_pred = max(emotion_scores, key=emotion_scores.get) | |
return emotion_pred | |
def generate_text(prompt, emotion=None, max_length=100): | |
""" | |
Generates text using a fine-tuned language model, optionally incorporating the specified emotion. | |
Args: | |
prompt (str): The starting prompt for text generation. | |
emotion (str, optional): The emotion to consider during generation. Defaults to None. | |
max_length (int, optional): The maximum length of the generated text. Defaults to 100. | |
Returns: | |
str: The generated text. | |
""" | |
finetuned_lm_tokenizer, finetuned_lm_model = get_finetuned_lm_model() | |
input_ids = finetuned_lm_tokenizer.encode(prompt, return_tensors='pt') | |
attention_mask = torch.ones(input_ids.shape, dtype=torch.long) | |
if torch.cuda.is_available(): | |
input_ids = input_ids.cuda() | |
attention_mask = attention_mask.cuda() | |
finetuned_lm_model = finetuned_lm_model.cuda() | |
if emotion: | |
emotion_token = emotion_prediction_tokenizer.encode(emotion, add_special_tokens=False) | |
input_ids = torch.cat((input_ids, torch.tensor(emotion_token).unsqueeze(0)), dim=1) | |
outputs = finetuned_lm_model.generate( | |
input_ids=input_ids, | |
attention_mask=attention_mask, | |
max_length=max_length, | |
pad_token_id=finetuned_lm_tokenizer.eos_token_id, | |
num_return_sequences=1, | |
temperature=0.7, | |
top_p=0.9, | |
do_sample=True | |
) | |
return finetuned_lm_tokenizer.decode(outputs[0], skip_special_tokens=True) | |
def optimize_ai_model(emotion_history): | |
""" | |
Trains a Random Forest classifier to predict emotions based on historical data. | |
This function is optional and can be used to improve the accuracy of emotion prediction | |
over time by learning from past interactions. | |
Args: | |
emotion_history (list): A list of dictionaries containing context and emotion information. | |
Returns: | |
tuple: A tuple containing the trained classifier and its accuracy score (or None if insufficient data). | |
""" | |
if len(emotion_history) < 2: | |
return None, None | |
contexts = [entry['context'] for entry in emotion_history] | |
emotions = [entry['emotion'] for entry in emotion_history] | |
encoder = OneHotEncoder(handle_unknown='ignore', sparse=False) | |
X = encoder.fit_transform(np.array(contexts).reshape(-1, 1)) | |
y = np.array(pd.Categorical(emotions). | |
codes) | |
clf = RandomForestClassifier(n_estimators=100) | |
clf.fit(X, y) | |
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) | |
accuracy = clf.score(X_test, y_test) | |
return clf, accuracy | |
def get_dominant_emotion(): | |
""" | |
Returns the emotion with the highest percentage. | |
Returns: | |
str: The emotion with the highest percentage. | |
""" | |
dominant_emotion = max(emotions, key=lambda e: emotions[e]['percentage']) | |
return dominant_emotion | |
def respond_to_user(context): | |
""" | |
Responds to the user based on the provided context. | |
This function predicts the user's emotion from the context, generates a response that | |
considers the user's emotional state, and updates the internal emotional states. | |
Args: | |
context (str): The user's input context. | |
Returns: | |
str: The AI's response to the user. | |
""" | |
predicted_emotion = predict_emotion(context) | |
dominant_emotion = get_dominant_emotion() | |
response = f"You seem to be feeling {predicted_emotion}. " | |
if predicted_emotion == dominant_emotion: | |
response += f"I understand that you're feeling strongly about {predicted_emotion} right now. " | |
else: | |
response += f"Is there anything I can do to help you with {predicted_emotion}? " | |
if dominant_emotion != 'ideal_state': | |
adjustment_percentage = min(emotions[dominant_emotion]['percentage'] / 2, 10) | |
update_emotion(dominant_emotion, -adjustment_percentage, 0) | |
emotion_history.append({'context': context, 'emotion': predicted_emotion}) | |
save_historical_data(emotion_history) | |
# Train the emotion prediction classifier if enough data is available | |
trained_clf, accuracy = optimize_ai_model(emotion_history) | |
if trained_clf: | |
print(f"Emotion prediction model accuracy: {accuracy:.2f}") | |
generated_text = generate_text(prompt=response, emotion=dominant_emotion) | |
return generated_text | |
interface = gr.Interface(respond_to_user, inputs="textbox", outputs="textbox", title="AI Assistant with Evolving Emotions", description="Talk to an AI that adapts to your emotions.") | |
interface.launch() | |
# Clean up memory usage | |
del finetuned_lm_tokenizer | |
del finetuned_lm_model | |
gc.collect() | |