| import os |
| import pandas as pd |
| import torch |
| import time |
| import json |
| import re |
| from transformers import AutoModelForCausalLM, AutoTokenizer |
| from peft import PeftModel |
| import matplotlib.pyplot as plt |
| import seaborn as sns |
| from sklearn.metrics import accuracy_score, f1_score, confusion_matrix |
|
|
| def load_qwen_model(): |
| device = "mps" if torch.backends.mps.is_available() else "cpu" |
| print(f"Loading on {device}...") |
| model_id = "Qwen/Qwen2.5-0.5B-Instruct" |
| adapter_path = "qwen2.5_local_mac_lora" |
| |
| tokenizer = AutoTokenizer.from_pretrained(model_id) |
| tokenizer.pad_token = tokenizer.eos_token |
| |
| base_model = AutoModelForCausalLM.from_pretrained( |
| model_id, |
| torch_dtype=torch.float16, |
| low_cpu_mem_usage=True |
| ).to(device) |
| model = PeftModel.from_pretrained(base_model, adapter_path) |
| return model, tokenizer, device |
|
|
| def evaluate_on_dataset(model, tokenizer, device, df, text_col='text'): |
| preds = [] |
| times = [] |
| |
| for idx, row in df.iterrows(): |
| text = row[text_col] |
| messages = [{"role": "user", "content": f"Analyse le sentiment de ce tweet : '{text}'"}] |
| prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) |
| inputs = tokenizer(prompt, return_tensors="pt").to(device) |
| |
| start_time = time.time() |
| with torch.no_grad(): |
| outputs = model.generate(**inputs, max_new_tokens=15, temperature=0.1) |
| inf_time = time.time() - start_time |
| |
| response = tokenizer.decode(outputs[0][len(inputs["input_ids"][0]):], skip_special_tokens=True).strip() |
| |
| if "positive" in response.lower(): |
| pred = 1 |
| else: |
| pred = 0 |
| |
| preds.append(pred) |
| times.append(inf_time) |
| |
| if idx % 50 == 0: |
| print(f"Processed {idx}/{len(df)}...") |
| |
| return preds, sum(times)/len(times) |
|
|
| def main(): |
| model, tokenizer, device = load_qwen_model() |
| |
| |
| print("Evaluating Benchmark V1...") |
| df_v1 = pd.read_csv("data/benchmark_results.csv") |
| preds, avg_time = evaluate_on_dataset(model, tokenizer, device, df_v1) |
| df_v1['qwen_pred'] = preds |
| df_v1.to_csv("data/benchmark_results.csv", index=False) |
| |
| qwen_acc_v1 = accuracy_score(df_v1['true_sentiment'], df_v1['qwen_pred']) |
| qwen_f1_v1 = f1_score(df_v1['true_sentiment'], df_v1['qwen_pred']) |
| |
| keras_acc_v1 = accuracy_score(df_v1['true_sentiment'], df_v1['keras_pred']) |
| llm_acc_v1 = accuracy_score(df_v1['true_sentiment'], df_v1['llm_pred']) |
| keras_f1_v1 = f1_score(df_v1['true_sentiment'], df_v1['keras_pred']) |
| llm_f1_v1 = f1_score(df_v1['true_sentiment'], df_v1['llm_pred']) |
| |
| |
| print("Evaluating Modern Benchmark V2...") |
| df_v2 = pd.read_csv("data/modern_benchmark_results.csv") |
| preds_v2, avg_time_v2 = evaluate_on_dataset(model, tokenizer, device, df_v2) |
| df_v2['qwen_pred'] = preds_v2 |
| df_v2.to_csv("data/modern_benchmark_results.csv", index=False) |
| |
| qwen_acc_v2 = accuracy_score(df_v2['true_sentiment'], df_v2['qwen_pred']) |
| keras_acc_v2 = accuracy_score(df_v2['true_sentiment'], df_v2['keras_pred']) |
| llm_acc_v2 = accuracy_score(df_v2['true_sentiment'], df_v2['llm_pred']) |
| |
| |
| print("Evaluating Human Ground Truth...") |
| df_human = pd.read_csv("data/human_ground_truth.csv") |
| preds_human, _ = evaluate_on_dataset(model, tokenizer, device, df_human) |
| df_human['qwen_pred'] = preds_human |
| df_human.to_csv("data/human_ground_truth.csv", index=False) |
| |
| qwen_human = accuracy_score(df_human['human_label'], df_human['qwen_pred']) |
| |
| |
| metrics = { |
| "qwen_acc_v1": qwen_acc_v1, |
| "qwen_f1_v1": qwen_f1_v1, |
| "qwen_acc_v2": qwen_acc_v2, |
| "qwen_human": qwen_human, |
| "qwen_time": avg_time, |
| "keras_acc_v1": keras_acc_v1, |
| "llm_acc_v1": llm_acc_v1, |
| "keras_f1_v1": keras_f1_v1, |
| "llm_f1_v1": llm_f1_v1, |
| "keras_acc_v2": keras_acc_v2, |
| "llm_acc_v2": llm_acc_v2 |
| } |
| with open("qwen_metrics.json", "w") as f: |
| json.dump(metrics, f) |
| |
| |
| plt.figure(figsize=(10, 6)) |
| models = ['Keras (Baseline)', 'Gemma 3 1B', 'Qwen 2.5 0.5B'] |
| accuracies = [keras_acc_v1*100, llm_acc_v1*100, qwen_acc_v1*100] |
| sns.barplot(x=models, y=accuracies, palette=['#ff5e62', '#00df89', '#00c3ff']) |
| plt.title('Accuracy Comparaison (Sentiment140 V1)') |
| plt.ylabel('Accuracy (%)') |
| plt.ylim(0, 100) |
| for i, acc in enumerate(accuracies): |
| plt.text(i, acc + 1, f'{acc:.1f}%', ha='center', fontweight='bold') |
| plt.savefig('assets/accuracy_comparison.png', dpi=300, bbox_inches='tight') |
| |
| |
| keras_time = 0.00013 |
| llm_time = 0.56 |
| plt.figure(figsize=(10, 6)) |
| times_plt = [keras_time, llm_time, avg_time] |
| sns.barplot(x=models, y=times_plt, palette=['#ff5e62', '#00df89', '#00c3ff']) |
| plt.title("Temps d'inférence moyen par tweet (secondes)") |
| plt.ylabel('Temps (s)') |
| for i, t in enumerate(times_plt): |
| plt.text(i, t + 0.02, f'{t:.3f}s', ha='center', fontweight='bold') |
| plt.savefig('assets/speed_comparison.png', dpi=300, bbox_inches='tight') |
| |
| |
| plt.figure(figsize=(10, 6)) |
| accuracies_v2 = [keras_acc_v2*100, llm_acc_v2*100, qwen_acc_v2*100] |
| sns.barplot(x=models, y=accuracies_v2, palette=['#ff5e62', '#00df89', '#00c3ff']) |
| plt.title('Sensibilité au Data Drift (Accuracy sur Tweets Modernes V2)') |
| plt.ylabel('Accuracy (%)') |
| plt.ylim(0, 100) |
| for i, acc in enumerate(accuracies_v2): |
| plt.text(i, acc + 1, f'{acc:.1f}%', ha='center', fontweight='bold') |
| plt.savefig('assets/modern_accuracy_comparison.png', dpi=300, bbox_inches='tight') |
|
|
| |
| fig, axes = plt.subplots(1, 3, figsize=(18, 5)) |
| cm_keras = confusion_matrix(df_v1['true_sentiment'], df_v1['keras_pred']) |
| cm_llm = confusion_matrix(df_v1['true_sentiment'], df_v1['llm_pred']) |
| cm_qwen = confusion_matrix(df_v1['true_sentiment'], df_v1['qwen_pred']) |
| |
| sns.heatmap(cm_keras, annot=True, fmt='d', cmap='Reds', ax=axes[0]) |
| axes[0].set_title('Keras Confusion Matrix') |
| axes[0].set_xlabel('Predicted') |
| axes[0].set_ylabel('True') |
| |
| sns.heatmap(cm_llm, annot=True, fmt='d', cmap='Greens', ax=axes[1]) |
| axes[1].set_title('Gemma 3 Confusion Matrix') |
| axes[1].set_xlabel('Predicted') |
| axes[1].set_ylabel('True') |
| |
| sns.heatmap(cm_qwen, annot=True, fmt='d', cmap='Blues', ax=axes[2]) |
| axes[2].set_title('Qwen Confusion Matrix') |
| axes[2].set_xlabel('Predicted') |
| axes[2].set_ylabel('True') |
| |
| plt.tight_layout() |
| plt.savefig('assets/confusion_matrices.png', dpi=300, bbox_inches='tight') |
| |
| print("Done! Check assets/ and data/ directories.") |
|
|
| if __name__ == "__main__": |
| main() |
|
|