Улучшен алгоритм расчета score: использование топ-k вместо среднего для большей чувствительности

This commit is contained in:
2026-01-28 00:16:57 +03:00
parent 67f3a1bb18
commit 83b2116616

View File

@@ -244,9 +244,9 @@ class VectorStore:
Рассчитывает скор на основе сходства с примерами.
Алгоритм:
1. Вычисляем среднее косинусное сходство с положительными примерами
2. Вычисляем среднее косинусное сходство с отрицательными примерами
3. Финальный скор = pos_sim / (pos_sim + neg_sim + eps)
1. Вычисляем косинусное сходство со всеми примерами
2. Используем топ-k ближайших примеров для более чувствительной оценки
3. Сравниваем топ-k положительных с топ-k отрицательными
Args:
vector: Векторное представление нового поста
@@ -275,28 +275,47 @@ class VectorStore:
# Косинусное сходство с положительными примерами
# Для нормализованных векторов это просто скалярное произведение
pos_similarities = np.dot(pos_matrix, normalized)
pos_sim = float(np.mean(pos_similarities))
# Косинусное сходство с отрицательными примерами
if self.negative_count > 0:
neg_matrix = np.array(self._negative_vectors)
neg_similarities = np.dot(neg_matrix, normalized)
neg_sim = float(np.mean(neg_similarities))
else:
neg_similarities = np.array([])
# Используем топ-k ближайших примеров для более чувствительной оценки
# k выбирается динамически: минимум 10, но не больше 20% от общего количества
k_pos = min(max(10, self.positive_count // 10), 50)
k_pos = min(k_pos, len(pos_similarities))
# Топ-k положительных примеров
top_k_pos_sim = float(np.mean(np.sort(pos_similarities)[-k_pos:]))
# Для отрицательных: если их меньше k, берем все, иначе топ-k
if len(neg_similarities) > 0:
k_neg = min(max(10, self.negative_count // 10), 50)
k_neg = min(k_neg, len(neg_similarities))
top_k_neg_sim = float(np.mean(np.sort(neg_similarities)[-k_neg:]))
else:
# Если нет отрицательных примеров, используем нейтральное значение
neg_sim = pos_sim # Нейтральный скор = 0.5
top_k_neg_sim = top_k_pos_sim # Нейтральный скор = 0.5
# === Вариант 1: neg/pos (разница между положительными и отрицательными) ===
diff = pos_sim - neg_sim
score_neg_pos = 0.5 + (diff * self.score_multiplier)
# === Вариант 1: neg/pos (разница между топ-k положительными и отрицательными) ===
# Используем более агрессивную нормализацию для малых различий
diff = top_k_pos_sim - top_k_neg_sim
# Адаптивный множитель: чем больше примеров, тем выше чувствительность
adaptive_multiplier = self.score_multiplier * (1.0 + min(1.0, (self.positive_count + self.negative_count) / 1000))
score_neg_pos = 0.5 + (diff * adaptive_multiplier)
score_neg_pos = max(0.0, min(1.0, score_neg_pos))
# === Вариант 2: pos only (только положительные, топ-k ближайших) ===
# Берём топ-5 ближайших положительных примеров
top_k = min(5, len(pos_similarities))
top_k_sim = float(np.mean(np.sort(pos_similarities)[-top_k:]))
top_5_k = min(5, len(pos_similarities))
top_5_sim = float(np.mean(np.sort(pos_similarities)[-top_5_k:]))
# Нормализуем: 0.85 -> 0.0, 0.95 -> 1.0 (типичный диапазон для BERT)
score_pos_only = (top_k_sim - 0.85) / 0.10
score_pos_only = (top_5_sim - 0.85) / 0.10
score_pos_only = max(0.0, min(1.0, score_pos_only))
# Основной скор — neg/pos
@@ -306,10 +325,27 @@ class VectorStore:
total_examples = self.positive_count + self.negative_count
confidence = min(1.0, total_examples / 1000)
# Дополнительная диагностическая информация
pos_mean = float(np.mean(pos_similarities))
pos_std = float(np.std(pos_similarities))
pos_min = float(np.min(pos_similarities))
pos_max = float(np.max(pos_similarities))
if len(neg_similarities) > 0:
neg_mean = float(np.mean(neg_similarities))
neg_std = float(np.std(neg_similarities))
neg_min = float(np.min(neg_similarities))
neg_max = float(np.max(neg_similarities))
else:
neg_mean = neg_std = neg_min = neg_max = 0.0
logger.info(
f"VectorStore: pos_sim={pos_sim:.4f}, neg_sim={neg_sim:.4f}, "
f"top_k_sim={top_k_sim:.4f}, score_neg_pos={score_neg_pos:.4f}, "
f"score_pos_only={score_pos_only:.4f}"
f"VectorStore: top_k_pos={k_pos}, top_k_neg={k_neg if len(neg_similarities) > 0 else 0}, "
f"top_k_pos_sim={top_k_pos_sim:.4f}, top_k_neg_sim={top_k_neg_sim:.4f}, "
f"diff={diff:.4f}, adaptive_mult={adaptive_multiplier:.2f}, "
f"score_neg_pos={score_neg_pos:.4f}, score_pos_only={score_pos_only:.4f}, "
f"pos_mean={pos_mean:.4f}±{pos_std:.4f}[{pos_min:.4f}-{pos_max:.4f}], "
f"neg_mean={neg_mean:.4f}±{neg_std:.4f}[{neg_min:.4f}-{neg_max:.4f}]"
)
return score, confidence, score_pos_only