Spaces:
Runtime error
Runtime error
Для деплоя на HF
Browse files- .~lock.kw_questions_tested.csv# +0 -1
- classifier_app.py +4 -4
- model_info.json +6 -6
- sdc_classifier.py +73 -36
.~lock.kw_questions_tested.csv#
DELETED
@@ -1 +0,0 @@
|
|
1 |
-
,docsa,docsa-HP-ProBook-450-G7,03.02.2025 12:17,file:///home/docsa/.config/libreoffice/4;
|
|
|
|
classifier_app.py
CHANGED
@@ -19,7 +19,7 @@ class Config:
|
|
19 |
DEFAULT_SIGNATURES_FILE: str = "signatures.npz"
|
20 |
CACHE_FILE: str = "embeddings_cache.db"
|
21 |
MODEL_INFO_FILE: str = "model_info.json"
|
22 |
-
DEFAULT_OPENAI_MODELS: List[str] = field(default_factory=lambda: ["text-embedding-3-large"
|
23 |
DEFAULT_LOCAL_MODEL: str = "cambridgeltl/SapBERT-from-PubMedBERT-fulltext"
|
24 |
|
25 |
config = Config()
|
@@ -33,8 +33,8 @@ class ClassifierApp:
|
|
33 |
"classes_info": {},
|
34 |
"errors": []
|
35 |
}
|
36 |
-
self.model_type = "Local" # Додати цей рядок
|
37 |
-
|
38 |
|
39 |
def initialize_environment(self) -> Tuple[Dict, Optional[SDCClassifier]]:
|
40 |
"""Ініціалізація середовища при першому запуску"""
|
@@ -56,7 +56,7 @@ class ClassifierApp:
|
|
56 |
with open(config.MODEL_INFO_FILE, 'r') as f:
|
57 |
model_info = json.load(f)
|
58 |
if not model_info.get('using_local', True):
|
59 |
-
signatures_model = "text-embedding-3-
|
60 |
|
61 |
# Створюємо класифікатор з тією ж моделлю
|
62 |
self.classifier = SDCClassifier(openai_api_key=os.getenv("OPENAI_API_KEY"))
|
|
|
19 |
DEFAULT_SIGNATURES_FILE: str = "signatures.npz"
|
20 |
CACHE_FILE: str = "embeddings_cache.db"
|
21 |
MODEL_INFO_FILE: str = "model_info.json"
|
22 |
+
DEFAULT_OPENAI_MODELS: List[str] = field(default_factory=lambda: ["text-embedding-3-large"])
|
23 |
DEFAULT_LOCAL_MODEL: str = "cambridgeltl/SapBERT-from-PubMedBERT-fulltext"
|
24 |
|
25 |
config = Config()
|
|
|
33 |
"classes_info": {},
|
34 |
"errors": []
|
35 |
}
|
36 |
+
# self.model_type = "Local" # Додати цей рядок
|
37 |
+
self.model_type = "OpenAI" # Нова версія
|
38 |
|
39 |
def initialize_environment(self) -> Tuple[Dict, Optional[SDCClassifier]]:
|
40 |
"""Ініціалізація середовища при першому запуску"""
|
|
|
56 |
with open(config.MODEL_INFO_FILE, 'r') as f:
|
57 |
model_info = json.load(f)
|
58 |
if not model_info.get('using_local', True):
|
59 |
+
signatures_model = "text-embedding-3-large" # Модель, яка використовувалась
|
60 |
|
61 |
# Створюємо класифікатор з тією ж моделлю
|
62 |
self.classifier = SDCClassifier(openai_api_key=os.getenv("OPENAI_API_KEY"))
|
model_info.json
CHANGED
@@ -3,15 +3,15 @@
|
|
3 |
"classes_count": 358,
|
4 |
"signatures_count": 358,
|
5 |
"cache_stats": {
|
6 |
-
"total_entries":
|
7 |
-
"cache_size_mb":
|
8 |
-
"hits":
|
9 |
-
"misses":
|
10 |
-
"hit_rate_percent":
|
11 |
},
|
12 |
"local_model": {
|
13 |
"model_name": "cambridgeltl/SapBERT-from-PubMedBERT-fulltext",
|
14 |
-
"device": "
|
15 |
"embedding_size": 768,
|
16 |
"max_length": 512,
|
17 |
"batch_size": 32
|
|
|
3 |
"classes_count": 358,
|
4 |
"signatures_count": 358,
|
5 |
"cache_stats": {
|
6 |
+
"total_entries": 29633,
|
7 |
+
"cache_size_mb": 179.21,
|
8 |
+
"hits": 0,
|
9 |
+
"misses": 0,
|
10 |
+
"hit_rate_percent": 0
|
11 |
},
|
12 |
"local_model": {
|
13 |
"model_name": "cambridgeltl/SapBERT-from-PubMedBERT-fulltext",
|
14 |
+
"device": "cpu",
|
15 |
"embedding_size": 768,
|
16 |
"max_length": 512,
|
17 |
"batch_size": 32
|
sdc_classifier.py
CHANGED
@@ -11,6 +11,7 @@ class SDCClassifier:
|
|
11 |
def __init__(self,
|
12 |
openai_api_key: str = None,
|
13 |
cache_path: str = "embeddings_cache.db",
|
|
|
14 |
local_model: str = "cambridgeltl/SapBERT-from-PubMedBERT-fulltext",
|
15 |
device: str = None):
|
16 |
"""
|
@@ -134,34 +135,47 @@ class SDCClassifier:
|
|
134 |
except (FileNotFoundError, IOError):
|
135 |
return None
|
136 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
137 |
def get_embedding(self, text: str, model_name: str = None) -> list:
|
138 |
"""
|
139 |
Отримання ембедінгу тексту
|
140 |
|
141 |
Args:
|
142 |
text: текст для ембедінгу
|
143 |
-
model_name: назва моделі (OpenAI) або None для
|
144 |
|
145 |
Returns:
|
146 |
list: ембедінг тексту
|
147 |
"""
|
148 |
# Перевіряємо кеш
|
149 |
-
|
|
|
150 |
if cached_embedding is not None:
|
151 |
return cached_embedding.tolist()
|
152 |
|
153 |
# Отримуємо ембедінг
|
154 |
-
if self.using_local
|
155 |
embedding = self.local_embedder.get_embeddings(text)[0]
|
156 |
else:
|
157 |
response = self.client.embeddings.create(
|
158 |
input=text,
|
159 |
-
model=model_name or "text-embedding-3-large"
|
160 |
)
|
161 |
embedding = response.data[0].embedding
|
162 |
|
163 |
# Зберігаємо в кеш
|
164 |
-
self.cache.put(text,
|
165 |
|
166 |
return embedding
|
167 |
|
@@ -430,8 +444,8 @@ class SDCClassifier:
|
|
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 |
|
@@ -440,50 +454,73 @@ class SDCClassifier:
|
|
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 |
-
|
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 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
485 |
|
486 |
-
return
|
487 |
|
488 |
def save_evaluation_results(self, df: pd.DataFrame, output_path: str = "evaluation_results.csv") -> str:
|
489 |
"""
|
|
|
11 |
def __init__(self,
|
12 |
openai_api_key: str = None,
|
13 |
cache_path: str = "embeddings_cache.db",
|
14 |
+
openai_model = None, # Модель OpenAI за замовчуванням
|
15 |
local_model: str = "cambridgeltl/SapBERT-from-PubMedBERT-fulltext",
|
16 |
device: str = None):
|
17 |
"""
|
|
|
135 |
except (FileNotFoundError, IOError):
|
136 |
return None
|
137 |
|
138 |
+
def set_openai_model(self, model_name: str) -> None:
|
139 |
+
"""
|
140 |
+
Встановлює модель OpenAI для використання
|
141 |
+
|
142 |
+
Args:
|
143 |
+
model_name: назва моделі OpenAI
|
144 |
+
"""
|
145 |
+
print(f"Встановлення OpenAI моделі: {model_name}")
|
146 |
+
self.using_local = False
|
147 |
+
self.local_embedder = None # Видаляємо локальний ембедер
|
148 |
+
self.openai_model = model_name # Зберігаємо назву моделі
|
149 |
+
|
150 |
def get_embedding(self, text: str, model_name: str = None) -> list:
|
151 |
"""
|
152 |
Отримання ембедінгу тексту
|
153 |
|
154 |
Args:
|
155 |
text: текст для ембедінгу
|
156 |
+
model_name: назва моделі (OpenAI) або None для використання поточної
|
157 |
|
158 |
Returns:
|
159 |
list: ембедінг тексту
|
160 |
"""
|
161 |
# Перевіряємо кеш
|
162 |
+
model_key = model_name or (self.openai_model if not self.using_local else "local")
|
163 |
+
cached_embedding = self.cache.get(text, model_key)
|
164 |
if cached_embedding is not None:
|
165 |
return cached_embedding.tolist()
|
166 |
|
167 |
# Отримуємо ембедінг
|
168 |
+
if self.using_local:
|
169 |
embedding = self.local_embedder.get_embeddings(text)[0]
|
170 |
else:
|
171 |
response = self.client.embeddings.create(
|
172 |
input=text,
|
173 |
+
model=model_name or self.openai_model or "text-embedding-3-large"
|
174 |
)
|
175 |
embedding = response.data[0].embedding
|
176 |
|
177 |
# Зберігаємо в кеш
|
178 |
+
self.cache.put(text, model_key, embedding)
|
179 |
|
180 |
return embedding
|
181 |
|
|
|
444 |
|
445 |
with open(path, 'w', encoding='utf-8') as f:
|
446 |
json.dump(info, f, indent=2)
|
447 |
+
|
448 |
+
def evaluate_classification(self, csv_path: str, threshold: float = 0.3) -> tuple[pd.DataFrame, dict]:
|
449 |
"""
|
450 |
Оцінка класифікації текстів з CSV файлу
|
451 |
|
|
|
454 |
threshold: поріг впевненості для класифікації
|
455 |
|
456 |
Returns:
|
457 |
+
tuple[pd.DataFrame, dict]: результати класифікації та статистика
|
458 |
"""
|
459 |
if self.class_signatures is None:
|
460 |
raise ValueError("Спочатку збудуйте signatures!")
|
461 |
|
462 |
# Завантаження даних
|
463 |
+
print(f"\nЗавантаження даних з {csv_path}...")
|
464 |
df = pd.read_csv(csv_path)
|
465 |
if not {'Category', 'Question'}.issubset(df.columns):
|
466 |
raise ValueError("CSV повинен містити колонки 'Category' та 'Question'")
|
467 |
|
468 |
# Підготовка результатів
|
469 |
results = []
|
470 |
+
total = len(df)
|
471 |
+
print(f"Знайдено {total} рядків для класифікації")
|
472 |
+
print(f"Використовується {'OpenAI' if not self.using_local else 'локальна'} модель")
|
473 |
|
474 |
for idx, row in df.iterrows():
|
475 |
+
if idx % 10 == 0: # Логуємо прогрес кожні 10 рядків
|
476 |
+
print(f"Обробка рядка {idx + 1}/{total}")
|
|
|
|
|
|
|
|
|
477 |
|
478 |
+
try:
|
479 |
+
# Отримуємо ембедінг для питання
|
480 |
+
emb = np.array(self.get_embedding(row['Question']))
|
481 |
+
|
482 |
+
# Нормалізуємо ембедінг
|
483 |
+
emb_norm = np.linalg.norm(emb)
|
484 |
+
if emb_norm > 0:
|
485 |
+
emb = emb / emb_norm
|
486 |
+
|
487 |
+
# Отримуємо всі передбачення
|
488 |
+
predictions = self.predict_classes(emb, threshold)
|
489 |
+
|
490 |
+
# Формуємо список класів за рівнем впевненості
|
491 |
+
sorted_classes = list(predictions.keys())
|
492 |
+
|
493 |
+
# Знаходимо позицію очікуваного класу
|
494 |
+
expected_class = row['Category']
|
495 |
+
expected_position = sorted_classes.index(expected_class) + 1 if expected_class in sorted_classes else -1
|
496 |
+
|
497 |
+
# Отримуємо рівень впевненості для очікуваного класу
|
498 |
+
expected_confidence = predictions.get(expected_class, 0.0)
|
499 |
+
|
500 |
+
# Додаємо результат
|
501 |
+
results.append({
|
502 |
+
'Category': row['Category'],
|
503 |
+
'Question': row['Question'],
|
504 |
+
'ExpectedClassPosition': expected_position,
|
505 |
+
'ExpectedClassConfidence': expected_confidence,
|
506 |
+
'ClassificationResults': json.dumps(predictions, ensure_ascii=False)
|
507 |
+
})
|
508 |
+
|
509 |
+
except Exception as e:
|
510 |
+
print(f"Помилка при обробці рядка {idx + 1}: {str(e)}")
|
511 |
+
results.append({
|
512 |
+
'Category': row['Category'],
|
513 |
+
'Question': row['Question'],
|
514 |
+
'ExpectedClassPosition': -1,
|
515 |
+
'ExpectedClassConfidence': 0.0,
|
516 |
+
'ClassificationResults': json.dumps({})
|
517 |
+
})
|
518 |
+
|
519 |
+
print("\nОбробка завершена")
|
520 |
+
results_df = pd.DataFrame(results)
|
521 |
+
statistics = self.get_evaluation_statistics(results_df)
|
522 |
|
523 |
+
return results_df, statistics
|
524 |
|
525 |
def save_evaluation_results(self, df: pd.DataFrame, output_path: str = "evaluation_results.csv") -> str:
|
526 |
"""
|