Sephfox commited on
Commit
60ae9bc
·
verified ·
1 Parent(s): 26bca4f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +146 -56
app.py CHANGED
@@ -50,6 +50,8 @@ emotion_prediction_tokenizer = AutoTokenizer.from_pretrained("bhadresh-savani/di
50
  # Lazy loading for the fine-tuned language model
51
  _finetuned_lm_tokenizer = None
52
  _finetuned_lm_model = None
 
 
53
  def get_finetuned_lm_model():
54
  global _finetuned_lm_tokenizer, _finetuned_lm_model
55
  if _finetuned_lm_tokenizer is None or _finetuned_lm_model is None:
@@ -58,6 +60,7 @@ def get_finetuned_lm_model():
58
  _finetuned_lm_model = AutoModelForCausalLM.from_pretrained(finetuned_lm_model_name, device_map="auto", low_cpu_mem_usage=True)
59
  return _finetuned_lm_tokenizer, _finetuned_lm_model
60
 
 
61
  # Enhanced Emotional States
62
  emotions = {
63
  'joy': {'percentage': 10, 'motivation': 'positive', 'intensity': 0},
@@ -69,7 +72,7 @@ emotions = {
69
  'determination': {'percentage': 10, 'motivation': 'positive', 'intensity': 0},
70
  'resentment': {'percentage': 10, 'motivation': 'negative', 'intensity': 0},
71
  'glory': {'percentage': 10, 'motivation': 'positive', 'intensity': 0},
72
- 'motivation': {'percentage': 10, 'motivation': 'positive', 'intensity': 0},
73
  'ideal_state': {'percentage': 100, 'motivation': 'balanced', 'intensity': 0},
74
  'fear': {'percentage': 10, 'motivation': 'defensive', 'intensity': 0},
75
  'surprise': {'percentage': 10, 'motivation': 'unexpected', 'intensity': 0},
@@ -84,19 +87,32 @@ emotions = {
84
  total_percentage = 200
85
  emotion_history_file = 'emotion_history.json'
86
 
 
87
  def load_historical_data(file_path=emotion_history_file):
88
  if os.path.exists(file_path):
89
  with open(file_path, 'r') as file:
90
  return json.load(file)
91
  return []
92
 
 
93
  def save_historical_data(historical_data, file_path=emotion_history_file):
94
  with open(file_path, 'w') as file:
95
  json.dump(historical_data, file)
96
 
 
97
  emotion_history = load_historical_data()
98
 
 
99
  def update_emotion(emotion, percentage, intensity):
 
 
 
 
 
 
 
 
 
100
  emotions['ideal_state']['percentage'] -= percentage
101
  emotions[emotion]['percentage'] += percentage
102
  emotions[emotion]['intensity'] = intensity
@@ -105,41 +121,58 @@ def update_emotion(emotion, percentage, intensity):
105
  adjustment = total_percentage - total_current
106
  emotions['ideal_state']['percentage'] += adjustment
107
 
 
108
  def normalize_context(context):
 
 
 
 
 
 
 
 
 
 
109
  return context.lower().strip()
110
 
111
- # Memory-efficient genetic algorithm for emotion evolution
 
112
  def evolve_emotions():
113
- def evaluate(individual):
114
- ideal_state = individual[-1]
115
- other_emotions = individual[:-1]
116
- intensities = individual[-21:-1]
117
- return (abs(ideal_state - 100),
118
- sum(other_emotions),
119
- max(intensities) - min(intensities))
120
-
121
- creator.create("FitnessMulti", base.Fitness, weights=(-1.0, -1.0, -1.0))
 
 
 
 
 
122
  creator.create("Individual", list, fitness=creator.FitnessMulti)
123
 
124
  toolbox = base.Toolbox()
125
  toolbox.register("attr_float", random.uniform, 0, 20)
126
  toolbox.register("attr_intensity", random.uniform, 0, 10)
127
  toolbox.register("individual", tools.initCycle, creator.Individual,
128
- (toolbox.attr_float,) * (len(emotions) - 1) +
129
- (toolbox.attr_intensity,) * len(emotions) +
130
  (lambda: 100,), n=1)
131
  toolbox.register("population", tools.initRepeat, list, toolbox.individual)
132
  toolbox.register("mate", tools.cxTwoPoint)
133
- toolbox.register("mutate", tools.mutGaussian, mu=0, sigma=1, indpb=0.2)
134
  toolbox.register("select", tools.selNSGA2)
135
  toolbox.register("evaluate", evaluate)
136
 
137
  population = toolbox.population(n=100)
138
- algorithms.eaMuPlusLambda(population, toolbox, mu=50, lambda_=100, cxpb=0.7, mutpb=0.2, ngen=100,
139
- stats=None, halloffame=None, verbose=False)
140
 
141
  best_individual = tools.selBest(population, k=1)[0]
142
- emotion_values = best_individual[:len(emotions)-1]
143
  intensities = best_individual[-21:-1]
144
  ideal_state = best_individual[-1]
145
 
@@ -149,18 +182,42 @@ def evolve_emotions():
149
 
150
  emotions['ideal_state']['percentage'] = ideal_state
151
 
 
152
  def predict_emotion(context):
 
 
 
 
 
 
 
 
 
 
153
  emotion_prediction_pipeline = pipeline('text-classification', model=emotion_prediction_model, tokenizer=emotion_prediction_tokenizer, top_k=None)
154
  predictions = emotion_prediction_pipeline(context)
155
  emotion_scores = {prediction['label']: prediction['score'] for prediction in predictions[0]}
156
  emotion_pred = max(emotion_scores, key=emotion_scores.get)
157
  return emotion_pred
158
 
 
159
  def generate_text(prompt, emotion=None, max_length=100):
 
 
 
 
 
 
 
 
 
 
 
 
160
  finetuned_lm_tokenizer, finetuned_lm_model = get_finetuned_lm_model()
161
  input_ids = finetuned_lm_tokenizer.encode(prompt, return_tensors='pt')
162
  attention_mask = torch.ones(input_ids.shape, dtype=torch.long)
163
-
164
  if torch.cuda.is_available():
165
  input_ids = input_ids.cuda()
166
  attention_mask = attention_mask.cuda()
@@ -183,7 +240,21 @@ def generate_text(prompt, emotion=None, max_length=100):
183
 
184
  return finetuned_lm_tokenizer.decode(outputs[0], skip_special_tokens=True)
185
 
 
186
  def optimize_ai_model(emotion_history):
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  if len(emotion_history) < 2:
188
  return None, None
189
 
@@ -192,54 +263,73 @@ def optimize_ai_model(emotion_history):
192
 
193
  encoder = OneHotEncoder(handle_unknown='ignore', sparse=False)
194
  X = encoder.fit_transform(np.array(contexts).reshape(-1, 1))
195
- y = np.array(pd.Categorical(emotions).codes)
 
 
 
196
 
197
- X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
198
-
199
- classifier = RandomForestClassifier(n_estimators=100, random_state=42)
200
- classifier.fit(X_train, y_train)
201
 
202
- score = classifier.score(X_test, y_test)
203
- return classifier, score
204
 
205
- def respond(context, conversation_history, emotion_history_state):
206
- normalized_context = normalize_context(context)
207
- emotion_pred = predict_emotion(normalized_context)
208
 
209
- emotion_history.append({
210
- 'context': normalized_context,
211
- 'emotion': emotion_pred
212
- })
213
- save_historical_data(emotion_history)
214
 
215
- emotion_model, score = optimize_ai_model(emotion_history)
 
 
216
 
217
- evolve_emotions()
 
218
 
219
- response_prompt = f"{normalized_context}\n\n{conversation_history}\n\n{emotion_pred}"
220
- generated_response = generate_text(response_prompt, emotion=emotion_pred)
221
 
222
- return generated_response, emotion_pred
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
 
224
- # GUI with Gradio
225
- with gr.Blocks() as demo:
226
- context_input = gr.Textbox(label="Context")
227
- response_output = gr.Textbox(label="Generated Response")
228
- emotion_output = gr.Textbox(label="Detected Emotion")
229
-
230
- conversation_history = gr.State([])
231
- emotion_history_state = gr.State([])
232
-
233
- def respond_wrapper(context, conversation_history, emotion_history_state):
234
- response, emotion = respond(context, conversation_history, emotion_history_state)
235
- conversation_history.append(response)
236
- emotion_history_state.append(emotion)
237
- return response, emotion, conversation_history, emotion_history_state
238
 
239
- submit_button = gr.Button("Submit")
240
- submit_button.click(respond_wrapper, [context_input, conversation_history, emotion_history_state], [response_output, emotion_output, conversation_history, emotion_history_state])
241
 
242
- demo.launch(share=True)
 
243
 
 
 
 
244
  gc.collect()
245
- torch.cuda.empty_cache()
 
50
  # Lazy loading for the fine-tuned language model
51
  _finetuned_lm_tokenizer = None
52
  _finetuned_lm_model = None
53
+
54
+
55
  def get_finetuned_lm_model():
56
  global _finetuned_lm_tokenizer, _finetuned_lm_model
57
  if _finetuned_lm_tokenizer is None or _finetuned_lm_model is None:
 
60
  _finetuned_lm_model = AutoModelForCausalLM.from_pretrained(finetuned_lm_model_name, device_map="auto", low_cpu_mem_usage=True)
61
  return _finetuned_lm_tokenizer, _finetuned_lm_model
62
 
63
+
64
  # Enhanced Emotional States
65
  emotions = {
66
  'joy': {'percentage': 10, 'motivation': 'positive', 'intensity': 0},
 
72
  'determination': {'percentage': 10, 'motivation': 'positive', 'intensity': 0},
73
  'resentment': {'percentage': 10, 'motivation': 'negative', 'intensity': 0},
74
  'glory': {'percentage': 10, 'motivation': 'positive', 'intensity': 0},
75
+ 'motivation': {'percentage': 'motivation': {'percentage': 10, 'motivation': 'positive', 'intensity': 0},
76
  'ideal_state': {'percentage': 100, 'motivation': 'balanced', 'intensity': 0},
77
  'fear': {'percentage': 10, 'motivation': 'defensive', 'intensity': 0},
78
  'surprise': {'percentage': 10, 'motivation': 'unexpected', 'intensity': 0},
 
87
  total_percentage = 200
88
  emotion_history_file = 'emotion_history.json'
89
 
90
+
91
  def load_historical_data(file_path=emotion_history_file):
92
  if os.path.exists(file_path):
93
  with open(file_path, 'r') as file:
94
  return json.load(file)
95
  return []
96
 
97
+
98
  def save_historical_data(historical_data, file_path=emotion_history_file):
99
  with open(file_path, 'w') as file:
100
  json.dump(historical_data, file)
101
 
102
+
103
  emotion_history = load_historical_data()
104
 
105
+
106
  def update_emotion(emotion, percentage, intensity):
107
+ """
108
+ Updates the emotional state based on the provided emotion, percentage, and intensity.
109
+
110
+ Args:
111
+ emotion (str): Name of the emotion to update.
112
+ percentage (float): Percentage change to apply to the emotion.
113
+ intensity (float): Intensity value to update for the emotion.
114
+ """
115
+
116
  emotions['ideal_state']['percentage'] -= percentage
117
  emotions[emotion]['percentage'] += percentage
118
  emotions[emotion]['intensity'] = intensity
 
121
  adjustment = total_percentage - total_current
122
  emotions['ideal_state']['percentage'] += adjustment
123
 
124
+
125
  def normalize_context(context):
126
+ """
127
+ Normalizes the context text by converting it to lowercase and removing whitespace.
128
+
129
+ Args:
130
+ context (str): The context text to normalize.
131
+
132
+ Returns:
133
+ str: The normalized context text.
134
+ """
135
+
136
  return context.lower().strip()
137
 
138
+
139
+ # Improved Genetic Algorithm for Emotion Evolution with clear function definition
140
  def evolve_emotions():
141
+ """
142
+ Evolves the emotional states using a genetic algorithm to find an optimal balance.
143
+
144
+ This function utilizes the DEAP library to implement a genetic algorithm. It aims to
145
+ find a combination of emotional states that minimizes the following criteria:
146
+
147
+ 1. Difference between the ideal state (100%) and its actual percentage.
148
+ 2. Sum of all non-ideal emotional percentages.
149
+ 3. Range of emotional intensities.
150
+
151
+ The weights for these criteria can be adjusted in the `creator.create("FitnessMulti", ...)` line.
152
+ """
153
+
154
+ creator.create("FitnessMulti", base.Fitness, weights=(-1.0, -0.5, -0.2)) # Weights for evaluation criteria
155
  creator.create("Individual", list, fitness=creator.FitnessMulti)
156
 
157
  toolbox = base.Toolbox()
158
  toolbox.register("attr_float", random.uniform, 0, 20)
159
  toolbox.register("attr_intensity", random.uniform, 0, 10)
160
  toolbox.register("individual", tools.initCycle, creator.Individual,
161
+ (toolbox.attr_float,) * (len(emotions) - 1) + # Individual emotions
162
+ (toolbox.attr_intensity,) * len(emotions) + # Intensities
163
  (lambda: 100,), n=1)
164
  toolbox.register("population", tools.initRepeat, list, toolbox.individual)
165
  toolbox.register("mate", tools.cxTwoPoint)
166
+ toolbox.register("mutate", tools.mutGaussian, mu=0, sigma=1, indpb=0.2
167
  toolbox.register("select", tools.selNSGA2)
168
  toolbox.register("evaluate", evaluate)
169
 
170
  population = toolbox.population(n=100)
171
+ algorithms.eaMuPlusLambda(population, toolbox, mu=50, lambda_=100, cxpb=0.7, mutpb=0.2, ngen=100,
172
+ stats=None, halloffame=None, verbose=False)
173
 
174
  best_individual = tools.selBest(population, k=1)[0]
175
+ emotion_values = best_individual[:len(emotions) - 1]
176
  intensities = best_individual[-21:-1]
177
  ideal_state = best_individual[-1]
178
 
 
182
 
183
  emotions['ideal_state']['percentage'] = ideal_state
184
 
185
+
186
  def predict_emotion(context):
187
+ """
188
+ Predicts the emotion from the provided context using a pre-trained BERT model.
189
+
190
+ Args:
191
+ context (str): The context text for emotion prediction.
192
+
193
+ Returns:
194
+ str: The predicted emotion.
195
+ """
196
+
197
  emotion_prediction_pipeline = pipeline('text-classification', model=emotion_prediction_model, tokenizer=emotion_prediction_tokenizer, top_k=None)
198
  predictions = emotion_prediction_pipeline(context)
199
  emotion_scores = {prediction['label']: prediction['score'] for prediction in predictions[0]}
200
  emotion_pred = max(emotion_scores, key=emotion_scores.get)
201
  return emotion_pred
202
 
203
+
204
  def generate_text(prompt, emotion=None, max_length=100):
205
+ """
206
+ Generates text using a fine-tuned language model, optionally incorporating the specified emotion.
207
+
208
+ Args:
209
+ prompt (str): The starting prompt for text generation.
210
+ emotion (str, optional): The emotion to consider during generation. Defaults to None.
211
+ max_length (int, optional): The maximum length of the generated text. Defaults to 100.
212
+
213
+ Returns:
214
+ str: The generated text.
215
+ """
216
+
217
  finetuned_lm_tokenizer, finetuned_lm_model = get_finetuned_lm_model()
218
  input_ids = finetuned_lm_tokenizer.encode(prompt, return_tensors='pt')
219
  attention_mask = torch.ones(input_ids.shape, dtype=torch.long)
220
+
221
  if torch.cuda.is_available():
222
  input_ids = input_ids.cuda()
223
  attention_mask = attention_mask.cuda()
 
240
 
241
  return finetuned_lm_tokenizer.decode(outputs[0], skip_special_tokens=True)
242
 
243
+
244
  def optimize_ai_model(emotion_history):
245
+ """
246
+ Trains a Random Forest classifier to predict emotions based on historical data.
247
+
248
+ This function is optional and can be used to improve the accuracy of emotion prediction
249
+ over time by learning from past interactions.
250
+
251
+ Args:
252
+ emotion_history (list): A list of dictionaries containing context and emotion information.
253
+
254
+ Returns:
255
+ tuple: A tuple containing the trained classifier and its accuracy score (or None if insufficient data).
256
+ """
257
+
258
  if len(emotion_history) < 2:
259
  return None, None
260
 
 
263
 
264
  encoder = OneHotEncoder(handle_unknown='ignore', sparse=False)
265
  X = encoder.fit_transform(np.array(contexts).reshape(-1, 1))
266
+ y = np.array(pd.Categorical(emotions).
267
+ codes)
268
+ clf = RandomForestClassifier(n_estimators=100)
269
+ clf.fit(X, y)
270
 
271
+ X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
272
+ accuracy = clf.score(X_test, y_test)
 
 
273
 
274
+ return clf, accuracy
 
275
 
 
 
 
276
 
277
+ def get_dominant_emotion():
278
+ """
279
+ Returns the emotion with the highest percentage.
 
 
280
 
281
+ Returns:
282
+ str: The emotion with the highest percentage.
283
+ """
284
 
285
+ dominant_emotion = max(emotions, key=lambda e: emotions[e]['percentage'])
286
+ return dominant_emotion
287
 
 
 
288
 
289
+ def respond_to_user(context):
290
+ """
291
+ Responds to the user based on the provided context.
292
+
293
+ This function predicts the user's emotion from the context, generates a response that
294
+ considers the user's emotional state, and updates the internal emotional states.
295
+
296
+ Args:
297
+ context (str): The user's input context.
298
+
299
+ Returns:
300
+ str: The AI's response to the user.
301
+ """
302
+
303
+ predicted_emotion = predict_emotion(context)
304
+ dominant_emotion = get_dominant_emotion()
305
+
306
+ response = f"You seem to be feeling {predicted_emotion}. "
307
+
308
+ if predicted_emotion == dominant_emotion:
309
+ response += f"I understand that you're feeling strongly about {predicted_emotion} right now. "
310
+ else:
311
+ response += f"Is there anything I can do to help you with {predicted_emotion}? "
312
+
313
+ if dominant_emotion != 'ideal_state':
314
+ adjustment_percentage = min(emotions[dominant_emotion]['percentage'] / 2, 10)
315
+ update_emotion(dominant_emotion, -adjustment_percentage, 0)
316
+
317
+ emotion_history.append({'context': context, 'emotion': predicted_emotion})
318
+ save_historical_data(emotion_history)
319
+
320
+ # Train the emotion prediction classifier if enough data is available
321
+ trained_clf, accuracy = optimize_ai_model(emotion_history)
322
+ if trained_clf:
323
+ print(f"Emotion prediction model accuracy: {accuracy:.2f}")
324
 
325
+ generated_text = generate_text(prompt=response, emotion=dominant_emotion)
326
+ return generated_text
 
 
 
 
 
 
 
 
 
 
 
 
327
 
 
 
328
 
329
+ 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.")
330
+ interface.launch()
331
 
332
+ # Clean up memory usage
333
+ del finetuned_lm_tokenizer
334
+ del finetuned_lm_model
335
  gc.collect()