Spaces:
Runtime error
Runtime error
Оновлено Пакетний режим тестування. Робочий варіант
Browse files- .gitignore +2 -1
- .~lock.kw_questions_tested.csv# +1 -0
- app.py +22 -35
- classifier_app.py +86 -1
- model_info.json +2 -2
- sdc_classifier.py +179 -0
.gitignore
CHANGED
@@ -4,4 +4,5 @@ __pycache__/
|
|
4 |
.env
|
5 |
|
6 |
*.npz
|
7 |
-
*.db
|
|
|
|
4 |
.env
|
5 |
|
6 |
*.npz
|
7 |
+
*.db
|
8 |
+
*.csv
|
.~lock.kw_questions_tested.csv#
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
,docsa,docsa-HP-ProBook-450-G7,03.02.2025 12:17,file:///home/docsa/.config/libreoffice/4;
|
app.py
CHANGED
@@ -85,24 +85,13 @@ def create_interface(app: ClassifierApp) -> gr.Blocks:
|
|
85 |
|
86 |
# Вкладка 3: Пакетна обробка
|
87 |
with gr.TabItem("Пакетна обробка"):
|
88 |
-
gr.Markdown("##
|
89 |
with gr.Row():
|
90 |
-
csv_input = gr.
|
91 |
-
|
92 |
-
|
93 |
)
|
94 |
-
|
95 |
-
value="embeddings.npy",
|
96 |
-
label="Numpy Embeddings"
|
97 |
-
)
|
98 |
-
load_btn = gr.Button("Завантажити дані")
|
99 |
-
|
100 |
-
load_output = gr.Label(label="Результат завантаження")
|
101 |
-
|
102 |
-
gr.Markdown("## 2) Класифікація")
|
103 |
-
with gr.Row():
|
104 |
-
filter_in = gr.Textbox(label="Фільтр (опціонально)")
|
105 |
-
batch_threshold = gr.Slider(
|
106 |
minimum=0.0,
|
107 |
maximum=1.0,
|
108 |
value=0.3,
|
@@ -110,12 +99,17 @@ def create_interface(app: ClassifierApp) -> gr.Blocks:
|
|
110 |
label="Поріг впевненості"
|
111 |
)
|
112 |
|
113 |
-
|
114 |
-
classify_out = gr.Dataframe(label="Результат (Message / Target / Scores)")
|
115 |
|
116 |
-
gr.
|
117 |
-
|
118 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
119 |
|
120 |
# Підключення обробників подій
|
121 |
model_type.change(
|
@@ -156,22 +150,15 @@ def create_interface(app: ClassifierApp) -> gr.Blocks:
|
|
156 |
outputs=result_text
|
157 |
)
|
158 |
|
159 |
-
|
160 |
-
fn=app.
|
161 |
-
inputs=[csv_input,
|
162 |
-
outputs=
|
163 |
-
)
|
164 |
-
|
165 |
-
classify_btn.click(
|
166 |
-
fn=app.classify_batch,
|
167 |
-
inputs=[filter_in, batch_threshold],
|
168 |
-
outputs=classify_out
|
169 |
)
|
170 |
|
171 |
-
|
172 |
-
fn=app.
|
173 |
-
|
174 |
-
outputs=save_out
|
175 |
)
|
176 |
|
177 |
return demo
|
|
|
85 |
|
86 |
# Вкладка 3: Пакетна обробка
|
87 |
with gr.TabItem("Пакетна обробка"):
|
88 |
+
gr.Markdown("## Оцінка класифікації")
|
89 |
with gr.Row():
|
90 |
+
csv_input = gr.File(
|
91 |
+
label="CSV файл з колонками Category та Question",
|
92 |
+
file_types=[".csv"]
|
93 |
)
|
94 |
+
threshold_slider_batch = gr.Slider(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
95 |
minimum=0.0,
|
96 |
maximum=1.0,
|
97 |
value=0.3,
|
|
|
99 |
label="Поріг впевненості"
|
100 |
)
|
101 |
|
102 |
+
evaluate_btn = gr.Button("Оцінити класифікацію")
|
|
|
103 |
|
104 |
+
results_df = gr.DataFrame(
|
105 |
+
label="Результати класифікації"
|
106 |
+
)
|
107 |
+
|
108 |
+
stats_md = gr.Markdown("### Статистика класифікації")
|
109 |
+
|
110 |
+
save_results_btn = gr.Button("Завантажити результати")
|
111 |
+
download_file = gr.File(label="Завантажити файл з результатами")
|
112 |
+
save_status = gr.Markdown()
|
113 |
|
114 |
# Підключення обробників подій
|
115 |
model_type.change(
|
|
|
150 |
outputs=result_text
|
151 |
)
|
152 |
|
153 |
+
evaluate_btn.click(
|
154 |
+
fn=app.evaluate_batch,
|
155 |
+
inputs=[csv_input, threshold_slider_batch],
|
156 |
+
outputs=[results_df, stats_md]
|
|
|
|
|
|
|
|
|
|
|
|
|
157 |
)
|
158 |
|
159 |
+
save_results_btn.click(
|
160 |
+
fn=app.save_evaluation_results,
|
161 |
+
outputs=[download_file, save_status]
|
|
|
162 |
)
|
163 |
|
164 |
return demo
|
classifier_app.py
CHANGED
@@ -5,13 +5,17 @@ import json
|
|
5 |
import os
|
6 |
from typing import Dict, Tuple, Optional, Any, List
|
7 |
from dataclasses import dataclass, field
|
|
|
8 |
|
9 |
# Load environment variables
|
10 |
load_dotenv()
|
11 |
|
12 |
@dataclass
|
13 |
class Config:
|
14 |
-
DEFAULT_CLASSES_FILE: str = "classes.json"
|
|
|
|
|
|
|
15 |
DEFAULT_SIGNATURES_FILE: str = "signatures.npz"
|
16 |
CACHE_FILE: str = "embeddings_cache.db"
|
17 |
MODEL_INFO_FILE: str = "model_info.json"
|
@@ -29,6 +33,7 @@ class ClassifierApp:
|
|
29 |
"classes_info": {},
|
30 |
"errors": []
|
31 |
}
|
|
|
32 |
|
33 |
def initialize_environment(self) -> Tuple[Dict, Optional[SDCClassifier]]:
|
34 |
"""Ініціалізація середовища при першому запуску"""
|
@@ -345,6 +350,86 @@ class ClassifierApp:
|
|
345 |
}
|
346 |
return self.initial_info
|
347 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
348 |
@staticmethod
|
349 |
def update_system_markdown(info: Dict) -> str:
|
350 |
"""Оновлення Markdown з системною інформацією"""
|
|
|
5 |
import os
|
6 |
from typing import Dict, Tuple, Optional, Any, List
|
7 |
from dataclasses import dataclass, field
|
8 |
+
import pandas as pd
|
9 |
|
10 |
# Load environment variables
|
11 |
load_dotenv()
|
12 |
|
13 |
@dataclass
|
14 |
class Config:
|
15 |
+
# DEFAULT_CLASSES_FILE: str = "classes.json"
|
16 |
+
DEFAULT_CLASSES_FILE: str = "kw_questions.json"
|
17 |
+
|
18 |
+
|
19 |
DEFAULT_SIGNATURES_FILE: str = "signatures.npz"
|
20 |
CACHE_FILE: str = "embeddings_cache.db"
|
21 |
MODEL_INFO_FILE: str = "model_info.json"
|
|
|
33 |
"classes_info": {},
|
34 |
"errors": []
|
35 |
}
|
36 |
+
self.model_type = "Local" # Додати цей рядок
|
37 |
|
38 |
def initialize_environment(self) -> Tuple[Dict, Optional[SDCClassifier]]:
|
39 |
"""Ініціалізація середовища при першому запуску"""
|
|
|
350 |
}
|
351 |
return self.initial_info
|
352 |
|
353 |
+
def evaluate_batch(self, csv_file, threshold: float) -> tuple[pd.DataFrame, str]:
|
354 |
+
"""
|
355 |
+
Оцінка пакетної класифікації
|
356 |
+
|
357 |
+
Args:
|
358 |
+
csv_file: завантажений CSV файл від gradio
|
359 |
+
threshold: поріг впевненості
|
360 |
+
|
361 |
+
Returns:
|
362 |
+
tuple[pd.DataFrame, str]: результати та статистика
|
363 |
+
"""
|
364 |
+
try:
|
365 |
+
if self.classifier is None:
|
366 |
+
return None, "Помилка: Класифікатор не ініціалізовано"
|
367 |
+
|
368 |
+
# Перевірка на None
|
369 |
+
if csv_file is None:
|
370 |
+
return None, "Помилка: Файл не завантажено"
|
371 |
+
|
372 |
+
# Зберігаємо тимчасовий файл
|
373 |
+
temp_path = "temp_upload.csv"
|
374 |
+
if hasattr(csv_file, 'name'):
|
375 |
+
# Якщо це файловий об'єкт від gradio
|
376 |
+
import shutil
|
377 |
+
shutil.copy2(csv_file.name, temp_path)
|
378 |
+
else:
|
379 |
+
# Якщо це шлях до файлу
|
380 |
+
temp_path = str(csv_file)
|
381 |
+
|
382 |
+
# Виконуємо класифікацію
|
383 |
+
results_df, statistics = self.classifier.evaluate_classification(temp_path, threshold)
|
384 |
+
|
385 |
+
# Формуємо текст статистики
|
386 |
+
stats_md = f"""### Статистика класифікації
|
387 |
+
- Всього зразків: {statistics['total_samples']}
|
388 |
+
- Правильний клас на першому місці: {statistics['correct_first_place']['count']} ({statistics['correct_first_place']['percentage']}%)
|
389 |
+
- Правильний клас в топ-3: {statistics['in_top3']['count']} ({statistics['in_top3']['percentage']}%)
|
390 |
+
- Правильний клас не знайдено: {statistics['not_found']['count']} ({statistics['not_found']['percentage']}%)
|
391 |
+
|
392 |
+
#### Середня впевненість для правильних класифікацій: {statistics['mean_confidence_correct']}%
|
393 |
+
|
394 |
+
#### Розподіл впевненості:
|
395 |
+
- 90-100%: {statistics['confidence_distribution']['90-100%']['count']} ({statistics['confidence_distribution']['90-100%']['percentage']}%)
|
396 |
+
- 70-90%: {statistics['confidence_distribution']['70-90%']['count']} ({statistics['confidence_distribution']['70-90%']['percentage']}%)
|
397 |
+
- 50-70%: {statistics['confidence_distribution']['50-70%']['count']} ({statistics['confidence_distribution']['50-70%']['percentage']}%)
|
398 |
+
- <50%: {statistics['confidence_distribution']['<50%']['count']} ({statistics['confidence_distribution']['<50%']['percentage']}%)
|
399 |
+
"""
|
400 |
+
|
401 |
+
# Зберігаємо результати для подальшого використання
|
402 |
+
self.current_evaluation_results = results_df
|
403 |
+
|
404 |
+
# Видаляємо тимчасовий файл якщо він був створений
|
405 |
+
if temp_path == "temp_upload.csv" and os.path.exists(temp_path):
|
406 |
+
os.remove(temp_path)
|
407 |
+
|
408 |
+
return results_df, stats_md
|
409 |
+
except Exception as e:
|
410 |
+
# У випадку помилки спробуємо видалити тимчасовий файл
|
411 |
+
if os.path.exists("temp_upload.csv"):
|
412 |
+
os.remove("temp_upload.csv")
|
413 |
+
return None, f"Помилка: {str(e)}"
|
414 |
+
|
415 |
+
def save_evaluation_results(self) -> tuple[str, str]:
|
416 |
+
"""
|
417 |
+
Зберігає результати останньої оцінки класифікації та готує файл для завантаження
|
418 |
+
|
419 |
+
Returns:
|
420 |
+
tuple[str, str]: (шлях до файлу, повідомлення про статус)
|
421 |
+
"""
|
422 |
+
try:
|
423 |
+
if not hasattr(self, 'current_evaluation_results'):
|
424 |
+
return None, "Помилка: Немає результатів для збереження"
|
425 |
+
|
426 |
+
output_path = "evaluation_results.csv"
|
427 |
+
self.current_evaluation_results.to_csv(output_path, index=False)
|
428 |
+
return output_path, f"Результати збережено у файл {output_path}"
|
429 |
+
|
430 |
+
except Exception as e:
|
431 |
+
return None, f"Помилка при збереженні: {str(e)}"
|
432 |
+
|
433 |
@staticmethod
|
434 |
def update_system_markdown(info: Dict) -> str:
|
435 |
"""Оновлення Markdown з системною інформацією"""
|
model_info.json
CHANGED
@@ -3,8 +3,8 @@
|
|
3 |
"classes_count": 0,
|
4 |
"signatures_count": 0,
|
5 |
"cache_stats": {
|
6 |
-
"total_entries":
|
7 |
-
"cache_size_mb":
|
8 |
"hits": 0,
|
9 |
"misses": 0,
|
10 |
"hit_rate_percent": 0
|
|
|
3 |
"classes_count": 0,
|
4 |
"signatures_count": 0,
|
5 |
"cache_stats": {
|
6 |
+
"total_entries": 7756,
|
7 |
+
"cache_size_mb": 30.84,
|
8 |
"hits": 0,
|
9 |
"misses": 0,
|
10 |
"hit_rate_percent": 0
|
sdc_classifier.py
CHANGED
@@ -430,7 +430,186 @@ class SDCClassifier:
|
|
430 |
|
431 |
with open(path, 'w', encoding='utf-8') as f:
|
432 |
json.dump(info, f, indent=2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
433 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
434 |
@staticmethod
|
435 |
def load_model_info(path: str) -> dict:
|
436 |
"""
|
|
|
430 |
|
431 |
with open(path, 'w', encoding='utf-8') as f:
|
432 |
json.dump(info, f, indent=2)
|
433 |
+
|
434 |
+
def evaluate_classification(self, csv_path: str, threshold: float = 0.3) -> pd.DataFrame:
|
435 |
+
"""
|
436 |
+
Оцінка класифікації текстів з CSV файлу
|
437 |
+
|
438 |
+
Args:
|
439 |
+
csv_path: шлях до CSV файлу з колонками Category та Question
|
440 |
+
threshold: поріг впевненості для класифікації
|
441 |
+
|
442 |
+
Returns:
|
443 |
+
pd.DataFrame: результати класифікації з додатковими метриками
|
444 |
+
"""
|
445 |
+
if self.class_signatures is None:
|
446 |
+
raise ValueError("Спочатку збудуйте signatures!")
|
447 |
+
|
448 |
+
# Завантаження даних
|
449 |
+
df = pd.read_csv(csv_path)
|
450 |
+
if not {'Category', 'Question'}.issubset(df.columns):
|
451 |
+
raise ValueError("CSV повинен містити колонки 'Category' та 'Question'")
|
452 |
+
|
453 |
+
# Підготовка результатів
|
454 |
+
results = []
|
455 |
+
|
456 |
+
for idx, row in df.iterrows():
|
457 |
+
# Отримуємо ембедінг для питання
|
458 |
+
emb = np.array(self.get_embedding(row['Question']))
|
459 |
+
|
460 |
+
# Нормалізуємо якщо потрібно
|
461 |
+
if self.embeddings_mean is not None and self.embeddings_std is not None and not self.using_local:
|
462 |
+
emb = (emb - self.embeddings_mean) / self.embeddings_std
|
463 |
+
|
464 |
+
# Отримуємо всі передбачення
|
465 |
+
predictions = self.predict_classes(emb, threshold)
|
466 |
+
|
467 |
+
# Формуємо список класів за рівнем впевненості
|
468 |
+
sorted_classes = list(predictions.keys())
|
469 |
+
|
470 |
+
# Знаходимо позицію очікуваного класу
|
471 |
+
expected_class = row['Category']
|
472 |
+
expected_position = sorted_classes.index(expected_class) + 1 if expected_class in sorted_classes else -1
|
473 |
+
|
474 |
+
# Отримуємо рівень впевненості для очікуваного класу
|
475 |
+
expected_confidence = predictions.get(expected_class, 0.0)
|
476 |
+
|
477 |
+
# Додаємо результат
|
478 |
+
results.append({
|
479 |
+
'Category': row['Category'],
|
480 |
+
'Question': row['Question'],
|
481 |
+
'ExpectedClassPosition': expected_position,
|
482 |
+
'ExpectedClassConfidence': expected_confidence,
|
483 |
+
'ClassificationResults': json.dumps(predictions)
|
484 |
+
})
|
485 |
+
|
486 |
+
return pd.DataFrame(results)
|
487 |
+
|
488 |
+
def save_evaluation_results(self, df: pd.DataFrame, output_path: str = "evaluation_results.csv") -> str:
|
489 |
+
"""
|
490 |
+
Зберігає результати оцінки класифікації
|
491 |
+
|
492 |
+
Args:
|
493 |
+
df: DataFrame з результатами
|
494 |
+
output_path: шлях для збереження файлу
|
495 |
+
|
496 |
+
Returns:
|
497 |
+
str: повідомлення про результат
|
498 |
+
"""
|
499 |
+
try:
|
500 |
+
df.to_csv(output_path, index=False)
|
501 |
+
return f"Результати збережено у файл {output_path}"
|
502 |
+
except Exception as e:
|
503 |
+
return f"Помилка при збереженні результатів: {str(e)}"
|
504 |
+
|
505 |
+
def get_evaluation_statistics(self, df: pd.DataFrame) -> dict:
|
506 |
+
"""
|
507 |
+
Розраховує статистику по результатам класифікації
|
508 |
+
|
509 |
+
Args:
|
510 |
+
df: DataFrame з результатами класифікації
|
511 |
|
512 |
+
Returns:
|
513 |
+
dict: статистика класифікації
|
514 |
+
"""
|
515 |
+
total = len(df)
|
516 |
+
found_mask = df['ExpectedClassPosition'] != -1
|
517 |
+
correct_first = (df['ExpectedClassPosition'] == 1).sum()
|
518 |
+
in_top3 = (df['ExpectedClassPosition'].between(1, 3)).sum()
|
519 |
+
not_found = (~found_mask).sum()
|
520 |
+
|
521 |
+
# Середня впевненість для коректних класифікацій
|
522 |
+
mean_confidence = df[df['ExpectedClassPosition'] == 1]['ExpectedClassConfidence'].mean()
|
523 |
+
|
524 |
+
# Підрахунок по діапазонах впевненості
|
525 |
+
confidence_ranges = {
|
526 |
+
"90-100%": ((df['ExpectedClassConfidence'] >= 0.9) & found_mask).sum(),
|
527 |
+
"70-90%": ((df['ExpectedClassConfidence'].between(0.7, 0.9)) & found_mask).sum(),
|
528 |
+
"50-70%": ((df['ExpectedClassConfidence'].between(0.5, 0.7)) & found_mask).sum(),
|
529 |
+
"<50%": ((df['ExpectedClassConfidence'] < 0.5) & found_mask).sum()
|
530 |
+
}
|
531 |
+
|
532 |
+
return {
|
533 |
+
"total_samples": total,
|
534 |
+
"correct_first_place": {
|
535 |
+
"count": int(correct_first),
|
536 |
+
"percentage": round(correct_first/total * 100, 1)
|
537 |
+
},
|
538 |
+
"in_top3": {
|
539 |
+
"count": int(in_top3),
|
540 |
+
"percentage": round(in_top3/total * 100, 1)
|
541 |
+
},
|
542 |
+
"not_found": {
|
543 |
+
"count": int(not_found),
|
544 |
+
"percentage": round(not_found/total * 100, 1)
|
545 |
+
},
|
546 |
+
"mean_confidence_correct": round(mean_confidence * 100, 1) if not np.isnan(mean_confidence) else 0,
|
547 |
+
"confidence_distribution": {
|
548 |
+
k: {
|
549 |
+
"count": int(v),
|
550 |
+
"percentage": round(v/total * 100, 1)
|
551 |
+
}
|
552 |
+
for k, v in confidence_ranges.items()
|
553 |
+
}
|
554 |
+
}
|
555 |
+
|
556 |
+
def evaluate_classification(self, csv_path: str, threshold: float = 0.3) -> tuple[pd.DataFrame, dict]:
|
557 |
+
"""
|
558 |
+
Оцінка класифікації текстів з CSV файлу
|
559 |
+
|
560 |
+
Args:
|
561 |
+
csv_path: шлях до CSV файлу з колонками Category та Question
|
562 |
+
threshold: поріг впевненості для класифікації
|
563 |
+
|
564 |
+
Returns:
|
565 |
+
tuple[pd.DataFrame, dict]: результати класифікації та статистика
|
566 |
+
"""
|
567 |
+
if self.class_signatures is None:
|
568 |
+
raise ValueError("Спочатку збудуйте signatures!")
|
569 |
+
|
570 |
+
# Завантаження даних
|
571 |
+
df = pd.read_csv(csv_path)
|
572 |
+
if not {'Category', 'Question'}.issubset(df.columns):
|
573 |
+
raise ValueError("CSV повинен містити колонки 'Category' та 'Question'")
|
574 |
+
|
575 |
+
# Підготовка результатів
|
576 |
+
results = []
|
577 |
+
|
578 |
+
for idx, row in df.iterrows():
|
579 |
+
# Отримуємо ембедінг для питання
|
580 |
+
emb = np.array(self.get_embedding(row['Question']))
|
581 |
+
|
582 |
+
# Нормалізуємо якщо потрібно
|
583 |
+
if self.embeddings_mean is not None and self.embeddings_std is not None and not self.using_local:
|
584 |
+
emb = (emb - self.embeddings_mean) / self.embeddings_std
|
585 |
+
|
586 |
+
# Отримуємо всі передбачення
|
587 |
+
predictions = self.predict_classes(emb, threshold)
|
588 |
+
|
589 |
+
# Формуємо список класів за рівнем впевненості
|
590 |
+
sorted_classes = list(predictions.keys())
|
591 |
+
|
592 |
+
# Знаходимо позицію очікуваного класу
|
593 |
+
expected_class = row['Category']
|
594 |
+
expected_position = sorted_classes.index(expected_class) + 1 if expected_class in sorted_classes else -1
|
595 |
+
|
596 |
+
# Отримуємо рівень впевненості для очікуваного класу
|
597 |
+
expected_confidence = predictions.get(expected_class, 0.0)
|
598 |
+
|
599 |
+
# Додаємо результат
|
600 |
+
results.append({
|
601 |
+
'Category': row['Category'],
|
602 |
+
'Question': row['Question'],
|
603 |
+
'ExpectedClassPosition': expected_position,
|
604 |
+
'ExpectedClassConfidence': expected_confidence,
|
605 |
+
'ClassificationResults': json.dumps(predictions)
|
606 |
+
})
|
607 |
+
|
608 |
+
results_df = pd.DataFrame(results)
|
609 |
+
statistics = self.get_evaluation_statistics(results_df)
|
610 |
+
|
611 |
+
return results_df, statistics
|
612 |
+
|
613 |
@staticmethod
|
614 |
def load_model_info(path: str) -> dict:
|
615 |
"""
|