File size: 8,919 Bytes
0e7dd0f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
import sys
import random
import requests
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from datasets import load_dataset
from sklearn.metrics import classification_report, accuracy_score, confusion_matrix, roc_curve, auc

# --- KONFIGURACJA ---
API_URL = "http://127.0.0.1:8000"  # Adres Twojego backendu FastAPI
TEST_GUILD_ID = "eval_test_guild"   # Testowa gildia
SAMPLE_SIZE = 250                    # Liczba pr贸bek na klas臋 (50 FAKE i 50 REAL = 100 test贸w)
# ---------------------

def setup_test_guild():
    """Wysy艂a 偶膮danie konfiguracji gildii testowej, aby zapobiec SetupRequiredError."""
    print(f"[*] Konfigurowanie testowej gildii '{TEST_GUILD_ID}' na backendzie...")
    setup_payload = {
        "active_text_model": "bibbbu/multilingual-ai-human-detector_xlm-roberta-base",  # Model, kt贸ry chcemy przetestowa膰
        "active_image_model": "none",
        "log_channel_id": None,
        "multi_model_workflow": False
    }
    try:
        r = requests.post(f"{API_URL}/guilds/{TEST_GUILD_ID}/setup", json=setup_payload)
        if r.status_code == 200:
            print("[+] Testowa gildia skonfigurowana pomy艣lnie.")
        else:
            print(f"[-] B艂膮d konfiguracji gildii: {r.status_code} - {r.text}")
            sys.exit(1)
    except Exception as e:
        print(f"[-] Brak po艂膮czenia z FastAPI pod adresem {API_URL}. Upewnij si臋, 偶e serwer dzia艂a. B艂膮d: {e}")
        sys.exit(1)

def prepare_dataset(sample_size):
    """Pobiera zbi贸r HC3 z Hugging Face i tworzy zbalansowany zbi贸r testowy."""
    print("[*] Pobieranie zbioru Hello-SimpleAI/HC3 z Hugging Face...")
    try:
        # Pobieranie bezpiecznej wersji Parquet
        ds = load_dataset(
            "Hello-SimpleAI/HC3", 
            "default", 
            revision="refs/convert/parquet", 
            split="train"
        )
    except Exception as e:
        print(f"[-] Nie uda艂o si臋 pobra膰 zbioru z Hugging Face: {e}")
        sys.exit(1)
        
    human_texts = []
    ai_texts = []
    
    print("[*] Filtrowanie i przygotowywanie pr贸bek tekstowych...")
    for item in ds:
        # human_answers i chatgpt_answers s膮 listami string贸w
        for ans in item.get("human_answers", []):
            # Filtrujemy teksty: min 50 znak贸w (wym贸g FastAPI), maks 1000 znak贸w dla szybko艣ci
            if 50 <= len(ans) <= 1000:
                human_texts.append(ans)
        for ans in item.get("chatgpt_answers", []):
            if 50 <= len(ans) <= 1000:
                ai_texts.append(ans)
                
    # Losowanie zbalansowanej pr贸bki z ziarnem losowo艣ci (powtarzalno艣膰 testu)
    random.seed(42)
    human_selected = random.sample(human_texts, min(sample_size, len(human_texts)))
    ai_selected = random.sample(ai_texts, min(sample_size, len(ai_texts)))
    
    test_set = []
    for text in human_selected:
        test_set.append({"text": text, "is_fake_ground_truth": False})
    for text in ai_selected:
        test_set.append({"text": text, "is_fake_ground_truth": True})
        
    random.shuffle(test_set)
    
    return test_set  # <-- TA LINIA MUSI BY膯 NA KO艃CU FUNKCJI

def run_evaluation(test_set):
    """Przeprowadza testy wysy艂aj膮c zapytania do endpointu FastAPI."""
    raw_results = []
    total = len(test_set)
    
    print(f"[*] Rozpoczynanie wysy艂ki {total} 偶膮da艅 do FastAPI...")
    
    for i, item in enumerate(test_set):
        payload = {
            "guild_id": TEST_GUILD_ID,
            "user_id": f"eval_user_{i}",  # Obej艣cie limitera (unikalny u偶ytkownik na zapytanie)
            "text": item["text"],
            "content_type": "text"
        }
        
        try:
            r = requests.post(f"{API_URL}/analyze", json=payload)
            if r.status_code == 200:
                data = r.json()
                is_deepfake_pred = data["is_deepfake"]
                confidence = data["confidence"]
                analysis_time = data["analysis_time"]
                used_model = data["used_model"]
                
                raw_results.append({
                    "id": i,
                    "text_snippet": item["text"][:60].replace("\n", " ") + "...",
                    "ground_truth": item["is_fake_ground_truth"],
                    "predicted": is_deepfake_pred,
                    "confidence": confidence,
                    "analysis_time": analysis_time,
                    "used_model": used_model,
                    "status": "SUCCESS"
                })
                print(f"[{i+1}/{total}] OK | GT: {item['is_fake_ground_truth']} | PRED: {is_deepfake_pred} | Conf: {confidence:.2f}")
            else:
                print(f"[{i+1}/{total}] B艂膮d API ({r.status_code}): {r.text}")
                raw_results.append({"id": i, "status": f"API_ERROR_{r.status_code}", "ground_truth": item["is_fake_ground_truth"]})
        except Exception as e:
            print(f"[{i+1}/{total}] B艂膮d po艂膮czenia: {e}")
            raw_results.append({"id": i, "status": "CONNECTION_ERROR", "ground_truth": item["is_fake_ground_truth"]})
            
    return raw_results

def process_and_save_results(raw_results):
    """Wylicza metryki, zapisuje raporty oraz generuje wykresy."""
    df_all = pd.DataFrame(raw_results)
    df_all.to_csv("evaluation_raw_results.csv", index=False, encoding="utf-8")
    print("[+] Zapisano surowe wyniki do: evaluation_raw_results.csv")
    
    # Filtrujemy tylko pomy艣lne wykonania do wyliczenia statystyk
    df_success = df_all[df_all["status"] == "SUCCESS"].copy()
    if df_success.empty:
        print("[-] Brak pomy艣lnych wynik贸w analizy. Wykresy i raporty nie zostan膮 wygenerowane.")
        return
        
    y_true = df_success["ground_truth"].astype(bool).tolist()
    y_pred = df_success["predicted"].astype(bool).tolist()
    
    # Obliczamy ci膮g艂e prawdopodobie艅stwo przynale偶no艣ci do klasy FAKE (potrzebne do krzywej ROC)
    # Je艣li model przewidzia艂 FAKE (True): prawdopodobie艅stwo FAKE to 'confidence'
    # Je艣li model przewidzia艂 REAL (False): prawdopodobie艅stwo FAKE to '1.0 - confidence'
    y_prob_fake = []
    for _, row in df_success.iterrows():
        conf = row["confidence"]
        pred = row["predicted"]
        y_prob_fake.append(conf if pred else 1.0 - conf)
    
    acc = accuracy_score(y_true, y_pred)
    report = classification_report(y_true, y_pred, target_names=["REAL (Human)", "FAKE (AI)"])
    avg_time = df_success["analysis_time"].mean()
    
    # 1. Zapisywanie raportu tekstowego
    report_filename = "evaluation_summary_report.txt"
    with open(report_filename, "w", encoding="utf-8") as f:
        f.write("==================================================\n")
        f.write("      RAPORT JAKO艢CI US艁UGI DETEKCJI TEKSTU       \n")
        f.write("==================================================\n")
        f.write(f"Zanalizowano pomy艣lnie pr贸bki: {len(df_success)} / {len(df_all)}\n")
        f.write(f"Og贸lna dok艂adno艣膰 (Accuracy): {acc:.2%}\n")
        f.write(f"艢redni czas analizy: {avg_time:.3f} sekundy\n\n")
        f.write("Szczeg贸艂owe metryki klasyfikacji:\n")
        f.write(report)
        f.write("==================================================\n")
    print(f"[+] Zapisano tekstowy raport ko艅cowy do: {report_filename}")
    
    # Wy艣wietlenie raportu w konsoli
    print("\n" + "="*50 + "\n" + f"DOK艁ADNO艢膯 SYSTEMU: {acc:.2%}" + "\n" + "="*50)
    print(report)

    # 2. Wykres: Macierz Pomy艂ek (Confusion Matrix)
    cm = confusion_matrix(y_true, y_pred)
    plt.figure(figsize=(6, 5))
    sns.heatmap(
        cm, annot=True, fmt="d", cmap="Blues",
        xticklabels=["REAL (Human)", "FAKE (AI)"],
        yticklabels=["REAL (Human)", "FAKE (AI)"]
    )
    plt.title("Macierz Pomy艂ek (Confusion Matrix)")
    plt.ylabel("Warto艣膰 Rzeczywista")
    plt.xlabel("Warto艣膰 Przewidziana")
    plt.tight_layout()
    plt.savefig("confusion_matrix.png")
    print("[+] Wygenerowano wykres: confusion_matrix.png")
    plt.close()

    # 3. Wykres: Krzywa ROC
    fpr, tpr, _ = roc_curve(y_true, y_prob_fake)
    roc_auc = auc(fpr, tpr)
    
    plt.figure(figsize=(6, 5))
    plt.plot(fpr, tpr, color="darkorange", lw=2, label=f"Krzywa ROC (AUC = {roc_auc:.2f})")
    plt.plot([0, 1], [0, 1], color="navy", lw=2, linestyle="--")
    plt.xlim([0.0, 1.0])
    plt.ylim([0.0, 1.05])
    plt.xlabel("False Positive Rate (1 - Specyficzno艣膰)")
    plt.ylabel("True Positive Rate (Czu艂o艣膰 / Recall)")
    plt.title("Krzywa ROC (Receiver Operating Characteristic)")
    plt.legend(loc="lower right")
    plt.tight_layout()
    plt.savefig("roc_curve.png")
    print("[+] Wygenerowano wykres: roc_curve.png")
    plt.close()

if __name__ == "__main__":
    setup_test_guild()
    test_set = prepare_dataset(SAMPLE_SIZE)
    raw_results = run_evaluation(test_set)
    process_and_save_results(raw_results)