Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -41,23 +41,20 @@ emotions_target = pd.Categorical(df['emotion']).codes
|
|
41 |
emotion_classes = pd.Categorical(df['emotion']).categories
|
42 |
|
43 |
# Load pre-trained BERT model for emotion prediction
|
44 |
-
emotion_prediction_model = AutoModelForSequenceClassification.from_pretrained("
|
45 |
-
emotion_prediction_tokenizer = AutoTokenizer.from_pretrained("
|
46 |
|
47 |
-
# Lazy loading for the fine-tuned language model
|
48 |
_finetuned_lm_tokenizer = None
|
49 |
_finetuned_lm_model = None
|
50 |
|
51 |
-
|
52 |
def get_finetuned_lm_model():
|
53 |
global _finetuned_lm_tokenizer, _finetuned_lm_model
|
54 |
if _finetuned_lm_tokenizer is None or _finetuned_lm_model is None:
|
55 |
-
|
56 |
-
|
57 |
-
_finetuned_lm_model = AutoModelForCausalLM.from_pretrained(finetuned_lm_model_name, device_map="auto", low_cpu_mem_usage=True)
|
58 |
return _finetuned_lm_tokenizer, _finetuned_lm_model
|
59 |
|
60 |
-
|
61 |
# Enhanced Emotional States
|
62 |
emotions = {
|
63 |
'joy': {'percentage': 10, 'motivation': 'positive', 'intensity': 0},
|
@@ -65,7 +62,7 @@ emotions = {
|
|
65 |
'sadness': {'percentage': 10, 'motivation': 'negative', 'intensity': 0},
|
66 |
'grief': {'percentage': 10, 'motivation': 'negative', 'intensity': 0},
|
67 |
'anger': {'percentage': 10, 'motivation': 'traumatic or strong', 'intensity': 0},
|
68 |
-
'calmness': {'percentage': 10, 'motivation': 'neutral', 'intensity': 0},
|
69 |
'determination': {'percentage': 10, 'motivation': 'positive', 'intensity': 0},
|
70 |
'resentment': {'percentage': 10, 'motivation': 'negative', 'intensity': 0},
|
71 |
'glory': {'percentage': 10, 'motivation': 'positive', 'intensity': 0},
|
@@ -84,32 +81,19 @@ emotions = {
|
|
84 |
total_percentage = 200
|
85 |
emotion_history_file = 'emotion_history.json'
|
86 |
|
87 |
-
|
88 |
def load_historical_data(file_path=emotion_history_file):
|
89 |
if os.path.exists(file_path):
|
90 |
with open(file_path, 'r') as file:
|
91 |
return json.load(file)
|
92 |
return []
|
93 |
|
94 |
-
|
95 |
def save_historical_data(historical_data, file_path=emotion_history_file):
|
96 |
with open(file_path, 'w') as file:
|
97 |
json.dump(historical_data, file)
|
98 |
|
99 |
-
|
100 |
emotion_history = load_historical_data()
|
101 |
|
102 |
-
|
103 |
def update_emotion(emotion, percentage, intensity):
|
104 |
-
"""
|
105 |
-
Updates the emotional state based on the provided emotion, percentage, and intensity.
|
106 |
-
|
107 |
-
Args:
|
108 |
-
emotion (str): Name of the emotion to update.
|
109 |
-
percentage (float): Percentage change to apply to the emotion.
|
110 |
-
intensity (float): Intensity value to update for the emotion.
|
111 |
-
"""
|
112 |
-
|
113 |
emotions['ideal_state']['percentage'] -= percentage
|
114 |
emotions[emotion]['percentage'] += percentage
|
115 |
emotions[emotion]['intensity'] = intensity
|
@@ -118,48 +102,32 @@ def update_emotion(emotion, percentage, intensity):
|
|
118 |
adjustment = total_percentage - total_current
|
119 |
emotions['ideal_state']['percentage'] += adjustment
|
120 |
|
121 |
-
|
122 |
def normalize_context(context):
|
123 |
-
"""
|
124 |
-
Normalizes the context text by converting it to lowercase and removing whitespace.
|
125 |
-
|
126 |
-
Args:
|
127 |
-
context (str): The context text to normalize.
|
128 |
-
|
129 |
-
Returns:
|
130 |
-
str: The normalized context text.
|
131 |
-
"""
|
132 |
-
|
133 |
return context.lower().strip()
|
134 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
135 |
|
136 |
-
# Improved Genetic Algorithm for Emotion Evolution with clear function definition
|
137 |
def evolve_emotions():
|
138 |
-
""
|
139 |
-
Evolves the emotional states using a genetic algorithm to find an optimal balance.
|
140 |
-
|
141 |
-
This function utilizes the DEAP library to implement a genetic algorithm. It aims to
|
142 |
-
find a combination of emotional states that minimizes the following criteria:
|
143 |
-
|
144 |
-
1. Difference between the ideal state (100%) and its actual percentage.
|
145 |
-
2. Sum of all non-ideal emotional percentages.
|
146 |
-
3. Range of emotional intensities.
|
147 |
-
|
148 |
-
The weights for these criteria can be adjusted in the `creator.create("FitnessMulti", ...)` line.
|
149 |
-
"""
|
150 |
-
|
151 |
-
creator.create("FitnessMulti", base.Fitness, weights=(-1.0, -0.5, -0.2)) # Weights for evaluation criteria
|
152 |
creator.create("Individual", list, fitness=creator.FitnessMulti)
|
153 |
|
154 |
toolbox = base.Toolbox()
|
155 |
toolbox.register("attr_float", random.uniform, 0, 20)
|
156 |
toolbox.register("attr_intensity", random.uniform, 0, 10)
|
157 |
toolbox.register("individual", tools.initCycle, creator.Individual,
|
158 |
-
(toolbox.attr_float,) * (len(emotions) - 1) +
|
159 |
-
(toolbox.attr_intensity,) * len(emotions) +
|
160 |
(lambda: 100,), n=1)
|
161 |
-
|
162 |
-
toolbox.register("mate", tools.cxTwoPoint)
|
163 |
toolbox.register("mutate", tools.mutGaussian, mu=0, sigma=1, indpb=0.2)
|
164 |
toolbox.register("select", tools.selNSGA2)
|
165 |
toolbox.register("evaluate", evaluate)
|
@@ -179,153 +147,73 @@ def evolve_emotions():
|
|
179 |
|
180 |
emotions['ideal_state']['percentage'] = ideal_state
|
181 |
|
182 |
-
|
183 |
def predict_emotion(context):
|
184 |
-
"""
|
185 |
-
Predicts the emotion from the provided context using a pre-trained BERT model.
|
186 |
-
|
187 |
-
Args:
|
188 |
-
context (str): The context text for emotion prediction.
|
189 |
-
|
190 |
-
Returns:
|
191 |
-
str: The predicted emotion.
|
192 |
-
"""
|
193 |
-
|
194 |
emotion_prediction_pipeline = pipeline('text-classification', model=emotion_prediction_model, tokenizer=emotion_prediction_tokenizer, top_k=None)
|
195 |
predictions = emotion_prediction_pipeline(context)
|
196 |
emotion_scores = {prediction['label']: prediction['score'] for prediction in predictions[0]}
|
197 |
emotion_pred = max(emotion_scores, key=emotion_scores.get)
|
198 |
return emotion_pred
|
199 |
|
200 |
-
|
201 |
def generate_text(prompt, emotion=None, max_length=100):
|
202 |
-
"""
|
203 |
-
Generates text using a fine-tuned language model, optionally incorporating the specified emotion.
|
204 |
-
|
205 |
-
Args:
|
206 |
-
prompt (str): The starting prompt for text generation.
|
207 |
-
emotion (str, optional): The emotion to consider during generation. Defaults to None.
|
208 |
-
max_length (int, optional): The maximum length of the generated text. Defaults to 100.
|
209 |
-
|
210 |
-
Returns:
|
211 |
-
str: The generated text.
|
212 |
-
"""
|
213 |
-
|
214 |
finetuned_lm_tokenizer, finetuned_lm_model = get_finetuned_lm_model()
|
215 |
-
input_ids = finetuned_lm_tokenizer.encode(prompt, return_tensors='pt')
|
216 |
-
|
217 |
-
|
218 |
if torch.cuda.is_available():
|
219 |
input_ids = input_ids.cuda()
|
220 |
-
attention_mask = attention_mask.cuda()
|
221 |
finetuned_lm_model = finetuned_lm_model.cuda()
|
222 |
|
|
|
|
|
|
|
223 |
if emotion:
|
224 |
-
|
225 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
226 |
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
max_length=max_length,
|
231 |
-
pad_token_id=finetuned_lm_tokenizer.eos_token_id,
|
232 |
num_return_sequences=1,
|
233 |
-
|
234 |
-
|
235 |
-
|
|
|
|
|
|
|
236 |
)
|
237 |
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
def optimize_ai_model(emotion_history):
|
242 |
-
"""
|
243 |
-
Trains a Random Forest classifier to predict emotions based on historical data.
|
244 |
-
|
245 |
-
This function is optional and can be used to improve the accuracy of emotion prediction
|
246 |
-
over time by learning from past interactions.
|
247 |
-
|
248 |
-
Args:
|
249 |
-
emotion_history (list): A list of dictionaries containing context and emotion information.
|
250 |
-
|
251 |
-
Returns:
|
252 |
-
tuple: A tuple containing the trained classifier and its accuracy score (or None if insufficient data).
|
253 |
-
"""
|
254 |
-
|
255 |
-
if len(emotion_history) < 2:
|
256 |
-
return None, None
|
257 |
-
|
258 |
-
contexts = [entry['context'] for entry in emotion_history]
|
259 |
-
emotions = [entry['emotion'] for entry in emotion_history]
|
260 |
-
|
261 |
-
encoder = OneHotEncoder(handle_unknown='ignore', sparse=False)
|
262 |
-
X = encoder.fit_transform(np.array(contexts).reshape(-1, 1))
|
263 |
-
y = np.array(pd.Categorical(emotions).codes)
|
264 |
-
clf = RandomForestClassifier(n_estimators=100)
|
265 |
-
clf.fit(X, y)
|
266 |
-
|
267 |
-
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
|
268 |
-
accuracy = clf.score(X_test, y_test)
|
269 |
-
|
270 |
-
return clf, accuracy
|
271 |
-
|
272 |
-
|
273 |
-
def get_dominant_emotion():
|
274 |
-
"""
|
275 |
-
Returns the emotion with the highest percentage.
|
276 |
-
|
277 |
-
Returns:
|
278 |
-
str: The emotion with the highest percentage.
|
279 |
-
"""
|
280 |
-
|
281 |
-
dominant_emotion = max(emotions, key=lambda e: emotions[e]['percentage'])
|
282 |
-
return dominant_emotion
|
283 |
-
|
284 |
-
|
285 |
-
def respond_to_user(context):
|
286 |
-
"""
|
287 |
-
Responds to the user based on the provided context.
|
288 |
-
|
289 |
-
This function predicts the user's emotion from the context, generates a response that
|
290 |
-
considers the user's emotional state, and updates the internal emotional states.
|
291 |
-
|
292 |
-
Args:
|
293 |
-
context (str): The user's input context.
|
294 |
-
|
295 |
-
Returns:
|
296 |
-
str: The AI's response to the user.
|
297 |
-
"""
|
298 |
-
|
299 |
-
predicted_emotion = predict_emotion(context)
|
300 |
-
dominant_emotion = get_dominant_emotion()
|
301 |
-
|
302 |
-
response = f"You seem to be feeling {predicted_emotion}. "
|
303 |
-
|
304 |
-
if predicted_emotion == dominant_emotion:
|
305 |
-
response += f"I understand that you're feeling strongly about {predicted_emotion} right now. "
|
306 |
-
else:
|
307 |
-
response += f"Is there anything I can do to help you with {predicted_emotion}? "
|
308 |
-
|
309 |
-
if dominant_emotion != 'ideal_state':
|
310 |
-
adjustment_percentage = min(emotions[dominant_emotion]['percentage'] / 2, 10)
|
311 |
-
update_emotion(dominant_emotion, -adjustment_percentage, 0)
|
312 |
-
|
313 |
-
emotion_history.append({'context': context, 'emotion': predicted_emotion})
|
314 |
-
save_historical_data(emotion_history)
|
315 |
-
|
316 |
-
# Train the emotion prediction classifier if enough data is available
|
317 |
-
trained_clf, accuracy = optimize_ai_model(emotion_history)
|
318 |
-
if trained_clf:
|
319 |
-
print(f"Emotion prediction model accuracy: {accuracy:.2f}")
|
320 |
-
|
321 |
-
generated_text = generate_text(prompt=response, emotion=dominant_emotion)
|
322 |
return generated_text
|
323 |
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
#
|
329 |
-
|
330 |
-
|
331 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
emotion_classes = pd.Categorical(df['emotion']).categories
|
42 |
|
43 |
# Load pre-trained BERT model for emotion prediction
|
44 |
+
emotion_prediction_model = AutoModelForSequenceClassification.from_pretrained("distilbert-base-uncased")
|
45 |
+
emotion_prediction_tokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased")
|
46 |
|
47 |
+
# Lazy loading for the fine-tuned language model (DialoGPT)
|
48 |
_finetuned_lm_tokenizer = None
|
49 |
_finetuned_lm_model = None
|
50 |
|
|
|
51 |
def get_finetuned_lm_model():
|
52 |
global _finetuned_lm_tokenizer, _finetuned_lm_model
|
53 |
if _finetuned_lm_tokenizer is None or _finetuned_lm_model is None:
|
54 |
+
_finetuned_lm_tokenizer = AutoTokenizer.from_pretrained("microsoft/DialoGPT-medium")
|
55 |
+
_finetuned_lm_model = AutoModelForCausalLM.from_pretrained("microsoft/DialoGPT-medium", device_map="auto", low_cpu_mem_usage=True)
|
|
|
56 |
return _finetuned_lm_tokenizer, _finetuned_lm_model
|
57 |
|
|
|
58 |
# Enhanced Emotional States
|
59 |
emotions = {
|
60 |
'joy': {'percentage': 10, 'motivation': 'positive', 'intensity': 0},
|
|
|
62 |
'sadness': {'percentage': 10, 'motivation': 'negative', 'intensity': 0},
|
63 |
'grief': {'percentage': 10, 'motivation': 'negative', 'intensity': 0},
|
64 |
'anger': {'percentage': 10, 'motivation': 'traumatic or strong', 'intensity': 0},
|
65 |
+
'calmness': {'percentage': 10, 'motivation': 'neutral', 'intensity': 0},'calmness': {'percentage': 10, 'motivation': 'neutral', 'intensity': 0},
|
66 |
'determination': {'percentage': 10, 'motivation': 'positive', 'intensity': 0},
|
67 |
'resentment': {'percentage': 10, 'motivation': 'negative', 'intensity': 0},
|
68 |
'glory': {'percentage': 10, 'motivation': 'positive', 'intensity': 0},
|
|
|
81 |
total_percentage = 200
|
82 |
emotion_history_file = 'emotion_history.json'
|
83 |
|
|
|
84 |
def load_historical_data(file_path=emotion_history_file):
|
85 |
if os.path.exists(file_path):
|
86 |
with open(file_path, 'r') as file:
|
87 |
return json.load(file)
|
88 |
return []
|
89 |
|
|
|
90 |
def save_historical_data(historical_data, file_path=emotion_history_file):
|
91 |
with open(file_path, 'w') as file:
|
92 |
json.dump(historical_data, file)
|
93 |
|
|
|
94 |
emotion_history = load_historical_data()
|
95 |
|
|
|
96 |
def update_emotion(emotion, percentage, intensity):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
97 |
emotions['ideal_state']['percentage'] -= percentage
|
98 |
emotions[emotion]['percentage'] += percentage
|
99 |
emotions[emotion]['intensity'] = intensity
|
|
|
102 |
adjustment = total_percentage - total_current
|
103 |
emotions['ideal_state']['percentage'] += adjustment
|
104 |
|
|
|
105 |
def normalize_context(context):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
106 |
return context.lower().strip()
|
107 |
|
108 |
+
def evaluate(individual):
|
109 |
+
emotion_values = individual[:len(emotions) - 1]
|
110 |
+
intensities = individual[-21:-1]
|
111 |
+
ideal_state = individual[-1]
|
112 |
+
|
113 |
+
ideal_diff = abs(100 - ideal_state)
|
114 |
+
sum_non_ideal = sum(emotion_values)
|
115 |
+
intensity_range = max(intensities) - min(intensities)
|
116 |
+
|
117 |
+
return ideal_diff, sum_non_ideal, intensity_range
|
118 |
|
|
|
119 |
def evolve_emotions():
|
120 |
+
creator.create("FitnessMulti", base.Fitness, weights=(-1.0, -0.5, -0.2))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
creator.create("Individual", list, fitness=creator.FitnessMulti)
|
122 |
|
123 |
toolbox = base.Toolbox()
|
124 |
toolbox.register("attr_float", random.uniform, 0, 20)
|
125 |
toolbox.register("attr_intensity", random.uniform, 0, 10)
|
126 |
toolbox.register("individual", tools.initCycle, creator.Individual,
|
127 |
+
(toolbox.attr_float,) * (len(emotions) - 1) +
|
128 |
+
(toolbox.attr_intensity,) * len(emotions) +
|
129 |
(lambda: 100,), n=1)
|
130 |
+
toolbox.register("mate", tools.cxTwoPoint)
|
|
|
131 |
toolbox.register("mutate", tools.mutGaussian, mu=0, sigma=1, indpb=0.2)
|
132 |
toolbox.register("select", tools.selNSGA2)
|
133 |
toolbox.register("evaluate", evaluate)
|
|
|
147 |
|
148 |
emotions['ideal_state']['percentage'] = ideal_state
|
149 |
|
|
|
150 |
def predict_emotion(context):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
151 |
emotion_prediction_pipeline = pipeline('text-classification', model=emotion_prediction_model, tokenizer=emotion_prediction_tokenizer, top_k=None)
|
152 |
predictions = emotion_prediction_pipeline(context)
|
153 |
emotion_scores = {prediction['label']: prediction['score'] for prediction in predictions[0]}
|
154 |
emotion_pred = max(emotion_scores, key=emotion_scores.get)
|
155 |
return emotion_pred
|
156 |
|
|
|
157 |
def generate_text(prompt, emotion=None, max_length=100):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
158 |
finetuned_lm_tokenizer, finetuned_lm_model = get_finetuned_lm_model()
|
159 |
+
input_ids = finetuned_lm_tokenizer.encode(prompt + finetuned_lm_tokenizer.eos_token, return_tensors='pt')
|
160 |
+
|
|
|
161 |
if torch.cuda.is_available():
|
162 |
input_ids = input_ids.cuda()
|
|
|
163 |
finetuned_lm_model = finetuned_lm_model.cuda()
|
164 |
|
165 |
+
attention_mask = torch.ones(input_ids.shape, dtype=torch.long, device=input_ids.device)
|
166 |
+
|
167 |
+
# Set up the emotion-specific generation parameters
|
168 |
if emotion:
|
169 |
+
# You can adjust these parameters based on the emotion
|
170 |
+
temperature = 0.7
|
171 |
+
top_k = 50
|
172 |
+
top_p = 0.9
|
173 |
+
else:
|
174 |
+
temperature = 1.0
|
175 |
+
top_k = 0
|
176 |
+
top_p = 1.0
|
177 |
|
178 |
+
# Generate the response
|
179 |
+
output = finetuned_lm_model.generate(
|
180 |
+
input_ids,
|
181 |
max_length=max_length,
|
|
|
182 |
num_return_sequences=1,
|
183 |
+
no_repeat_ngram_size=2,
|
184 |
+
do_sample=True,
|
185 |
+
temperature=temperature,
|
186 |
+
top_k=top_k,
|
187 |
+
top_p=top_p,
|
188 |
+
attention_mask=attention_mask
|
189 |
)
|
190 |
|
191 |
+
generated_text = finetuned_lm_tokenizer.decode(output[0], skip_special_tokens=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
192 |
return generated_text
|
193 |
|
194 |
+
def respond_to_user(user_input, chat_history):
|
195 |
+
# Predict the emotion from the user input
|
196 |
+
emotion = predict_emotion(user_input)
|
197 |
+
|
198 |
+
# Update the emotional state
|
199 |
+
update_emotion(emotion, 5, random.uniform(0, 10))
|
200 |
+
|
201 |
+
# Generate a response considering the emotion
|
202 |
+
response = generate_text(user_input, emotion)
|
203 |
+
|
204 |
+
# Update chat history
|
205 |
+
chat_history.append((user_input, response))
|
206 |
+
|
207 |
+
return response, chat_history
|
208 |
+
|
209 |
+
# Gradio interface
|
210 |
+
iface = gr.Interface(
|
211 |
+
fn=respond_to_user,
|
212 |
+
inputs=["text", "state"],
|
213 |
+
outputs=["text", "state"],
|
214 |
+
title="Emotion-Aware Chatbot",
|
215 |
+
description="Chat with an AI that understands and responds to emotions.",
|
216 |
+
)
|
217 |
+
|
218 |
+
if __name__ == "__main__":
|
219 |
+
iface.launch()
|