Spaces:
Sleeping
Sleeping
#!/usr/bin/env python | |
""" | |
Скрипт для построения специализированных графиков на основе макрометрик из Excel-файла. | |
Строит несколько типов графиков: | |
1. Зависимость macro_text_recall от top_N для разных моделей при фиксированных параметрах чанкинга | |
2. Зависимость macro_text_recall от top_N для разных подходов к чанкингу при фиксированных моделях | |
3. Зависимость macro_text_recall от подхода к чанкингу для разных моделей при фиксированных top_N | |
""" | |
import os | |
import matplotlib.pyplot as plt | |
import pandas as pd | |
import seaborn as sns | |
# Константы | |
EXCEL_FILE_PATH = "../../Белагропромбанк/test_vectors/combined_results.xlsx" | |
PLOTS_DIR = "../../Белагропромбанк/test_vectors/plots" | |
# Настройки для графиков | |
plt.rcParams['font.family'] = 'DejaVu Sans' | |
sns.set_style("whitegrid") | |
FIGSIZE = (14, 10) | |
DPI = 300 | |
def setup_plots_directory(plots_dir: str) -> None: | |
""" | |
Создает директорию для сохранения графиков, если она не существует. | |
Args: | |
plots_dir: Путь к директории для графиков | |
""" | |
if not os.path.exists(plots_dir): | |
os.makedirs(plots_dir) | |
print(f"Создана директория для графиков: {plots_dir}") | |
else: | |
print(f"Использование существующей директории для графиков: {plots_dir}") | |
def load_macro_metrics(excel_path: str) -> pd.DataFrame: | |
""" | |
Загружает макрометрики из Excel-файла. | |
Args: | |
excel_path: Путь к Excel-файлу с данными | |
Returns: | |
DataFrame с макрометриками | |
""" | |
try: | |
df = pd.read_excel(excel_path, sheet_name="Macro метрики") | |
print(f"Загружены данные из {excel_path}, лист 'Macro метрики'") | |
print(f"Количество строк: {len(df)}") | |
return df | |
except Exception as e: | |
print(f"Ошибка при загрузке данных: {e}") | |
raise | |
def plot_top_n_vs_recall_by_model(df: pd.DataFrame, plots_dir: str) -> None: | |
""" | |
Строит графики зависимости macro_text_recall от top_N для разных моделей | |
при фиксированных параметрах чанкинга (50/25 и 200/75). | |
Args: | |
df: DataFrame с данными | |
plots_dir: Директория для сохранения графиков | |
""" | |
# Фиксированные параметры чанкинга | |
chunking_params = [ | |
{"words": 50, "overlap": 25, "title": "Чанкинг 50/25"}, | |
{"words": 200, "overlap": 75, "title": "Чанкинг 200/75"} | |
] | |
# Создаем субплоты: 1 строка, 2 столбца | |
fig, axes = plt.subplots(1, 2, figsize=FIGSIZE, sharey=True) | |
for i, params in enumerate(chunking_params): | |
# Фильтруем данные для текущих параметров чанкинга | |
filtered_df = df[ | |
(df['words_per_chunk'] == params['words']) & | |
(df['overlap_words'] == params['overlap']) | |
] | |
if len(filtered_df) == 0: | |
print(f"Предупреждение: нет данных для чанкинга {params['words']}/{params['overlap']}") | |
axes[i].text(0.5, 0.5, f"Нет данных для чанкинга {params['words']}/{params['overlap']}", | |
ha='center', va='center', fontsize=12) | |
axes[i].set_title(params['title']) | |
continue | |
# Находим уникальные модели | |
models = filtered_df['model'].unique() | |
# Создаем палитру цветов | |
palette = sns.color_palette("viridis", len(models)) | |
# Строим график для каждой модели | |
for j, model in enumerate(models): | |
model_df = filtered_df[filtered_df['model'] == model].sort_values('top_n') | |
if len(model_df) <= 1: | |
print(f"Предупреждение: недостаточно данных для модели {model} при чанкинге {params['words']}/{params['overlap']}") | |
continue | |
# Строим ломаную линию | |
axes[i].plot(model_df['top_n'], model_df['macro_text_recall'], | |
marker='o', linestyle='-', linewidth=2, | |
label=model, color=palette[j]) | |
# Настраиваем оси и заголовок | |
axes[i].set_title(params['title'], fontsize=14) | |
axes[i].set_xlabel('top_N', fontsize=12) | |
if i == 0: | |
axes[i].set_ylabel('macro_text_recall', fontsize=12) | |
# Добавляем сетку | |
axes[i].grid(True, linestyle='--', alpha=0.7) | |
# Добавляем легенду | |
axes[i].legend(title="Модель", fontsize=10, loc='best') | |
# Общий заголовок | |
plt.suptitle('Зависимость macro_text_recall от top_N для разных моделей', fontsize=16) | |
# Настраиваем макет | |
plt.tight_layout(rect=[0, 0, 1, 0.96]) | |
# Сохраняем график | |
file_path = os.path.join(plots_dir, "top_n_vs_recall_by_model.png") | |
plt.savefig(file_path, dpi=DPI) | |
plt.close() | |
print(f"Создан график: {file_path}") | |
def plot_top_n_vs_recall_by_chunking(df: pd.DataFrame, plots_dir: str) -> None: | |
""" | |
Строит графики зависимости macro_text_recall от top_N для разных параметров чанкинга | |
при фиксированных моделях (bge и frida). | |
Args: | |
df: DataFrame с данными | |
plots_dir: Директория для сохранения графиков | |
""" | |
# Фиксированные модели | |
models = ["BAAI/bge", "frida"] | |
# Создаем субплоты: 1 строка, 2 столбца | |
fig, axes = plt.subplots(1, 2, figsize=FIGSIZE, sharey=True) | |
for i, model_name in enumerate(models): | |
# Находим все строки с моделями, содержащими указанное название | |
model_df = df[df['model'].str.contains(model_name, case=False)] | |
if len(model_df) == 0: | |
print(f"Предупреждение: нет данных для модели {model_name}") | |
axes[i].text(0.5, 0.5, f"Нет данных для модели {model_name}", | |
ha='center', va='center', fontsize=12) | |
axes[i].set_title(f"Модель: {model_name}") | |
continue | |
# Находим уникальные комбинации параметров чанкинга | |
chunking_combinations = model_df.drop_duplicates(['words_per_chunk', 'overlap_words'])[['words_per_chunk', 'overlap_words']] | |
# Ограничиваем количество комбинаций до 7 для читаемости | |
if len(chunking_combinations) > 7: | |
print(f"Предупреждение: слишком много комбинаций чанкинга для модели {model_name}, ограничиваем до 7") | |
chunking_combinations = chunking_combinations.head(7) | |
# Создаем палитру цветов | |
palette = sns.color_palette("viridis", len(chunking_combinations)) | |
# Строим график для каждой комбинации параметров чанкинга | |
for j, (_, row) in enumerate(chunking_combinations.iterrows()): | |
words = row['words_per_chunk'] | |
overlap = row['overlap_words'] | |
# Фильтруем данные для текущей модели и параметров чанкинга | |
chunking_df = model_df[ | |
(model_df['words_per_chunk'] == words) & | |
(model_df['overlap_words'] == overlap) | |
].sort_values('top_n') | |
if len(chunking_df) <= 1: | |
print(f"Предупреждение: недостаточно данных для модели {model_name} с чанкингом {words}/{overlap}") | |
continue | |
# Строим ломаную линию | |
axes[i].plot(chunking_df['top_n'], chunking_df['macro_text_recall'], | |
marker='o', linestyle='-', linewidth=2, | |
label=f"w={words}, o={overlap}", color=palette[j]) | |
# Настраиваем оси и заголовок | |
axes[i].set_title(f"Модель: {model_name}", fontsize=14) | |
axes[i].set_xlabel('top_N', fontsize=12) | |
if i == 0: | |
axes[i].set_ylabel('macro_text_recall', fontsize=12) | |
# Добавляем сетку | |
axes[i].grid(True, linestyle='--', alpha=0.7) | |
# Добавляем легенду | |
axes[i].legend(title="Чанкинг", fontsize=10, loc='best') | |
# Общий заголовок | |
plt.suptitle('Зависимость macro_text_recall от top_N для разных параметров чанкинга', fontsize=16) | |
# Настраиваем макет | |
plt.tight_layout(rect=[0, 0, 1, 0.96]) | |
# Сохраняем график | |
file_path = os.path.join(plots_dir, "top_n_vs_recall_by_chunking.png") | |
plt.savefig(file_path, dpi=DPI) | |
plt.close() | |
print(f"Создан график: {file_path}") | |
def plot_chunking_vs_recall_by_model(df: pd.DataFrame, plots_dir: str) -> None: | |
""" | |
Строит графики зависимости macro_text_recall от подхода к чанкингу | |
для разных моделей при фиксированных top_N (5, 20, 100). | |
Args: | |
df: DataFrame с данными | |
plots_dir: Директория для сохранения графиков | |
""" | |
# Фиксированные значения top_N | |
top_n_values = [5, 20, 100] | |
# Создаем субплоты: 1 строка, 3 столбца | |
fig, axes = plt.subplots(1, 3, figsize=FIGSIZE, sharey=True) | |
# Создаем порядок чанкинга - сортируем по возрастанию размера и оверлапа | |
chunking_order = df.drop_duplicates(['words_per_chunk', 'overlap_words'])[['words_per_chunk', 'overlap_words']] | |
chunking_order = chunking_order.sort_values(['words_per_chunk', 'overlap_words']) | |
# Создаем словарь для маппинга комбинаций чанкинга на индексы | |
chunking_labels = [f"{row['words_per_chunk']}/{row['overlap_words']}" for _, row in chunking_order.iterrows()] | |
chunking_map = {f"{row['words_per_chunk']}/{row['overlap_words']}": i for i, (_, row) in enumerate(chunking_order.iterrows())} | |
for i, top_n in enumerate(top_n_values): | |
# Фильтруем данные для текущего top_N | |
top_n_df = df[df['top_n'] == top_n] | |
if len(top_n_df) == 0: | |
print(f"Предупреждение: нет данных для top_N={top_n}") | |
axes[i].text(0.5, 0.5, f"Нет данных для top_N={top_n}", | |
ha='center', va='center', fontsize=12) | |
axes[i].set_title(f"top_N={top_n}") | |
continue | |
# Находим уникальные модели | |
models = top_n_df['model'].unique() | |
# Ограничиваем количество моделей до 5 для читаемости | |
if len(models) > 5: | |
print(f"Предупреждение: слишком много моделей для top_N={top_n}, ограничиваем до 5") | |
models = models[:5] | |
# Создаем палитру цветов | |
palette = sns.color_palette("viridis", len(models)) | |
# Строим график для каждой модели | |
for j, model in enumerate(models): | |
model_df = top_n_df[top_n_df['model'] == model].copy() | |
if len(model_df) <= 1: | |
print(f"Предупреждение: недостаточно данных для модели {model} при top_N={top_n}") | |
continue | |
# Создаем новую колонку с индексом чанкинга для сортировки | |
model_df['chunking_index'] = model_df.apply( | |
lambda row: chunking_map.get(f"{row['words_per_chunk']}/{row['overlap_words']}", -1), | |
axis=1 | |
) | |
# Отбрасываем строки с неизвестными комбинациями чанкинга | |
model_df = model_df[model_df['chunking_index'] >= 0] | |
if len(model_df) <= 1: | |
continue | |
# Сортируем по индексу чанкинга | |
model_df = model_df.sort_values('chunking_index') | |
# Создаем список индексов и значений для графика | |
x_indices = model_df['chunking_index'].tolist() | |
y_values = model_df['macro_text_recall'].tolist() | |
# Строим ломаную линию | |
axes[i].plot(x_indices, y_values, marker='o', linestyle='-', linewidth=2, | |
label=model, color=palette[j]) | |
# Настраиваем оси и заголовок | |
axes[i].set_title(f"top_N={top_n}", fontsize=14) | |
axes[i].set_xlabel('Подход к чанкингу', fontsize=12) | |
if i == 0: | |
axes[i].set_ylabel('macro_text_recall', fontsize=12) | |
# Устанавливаем метки на оси X (подходы к чанкингу) | |
axes[i].set_xticks(range(len(chunking_labels))) | |
axes[i].set_xticklabels(chunking_labels, rotation=45, ha='right', fontsize=10) | |
# Добавляем сетку | |
axes[i].grid(True, linestyle='--', alpha=0.7) | |
# Добавляем легенду | |
axes[i].legend(title="Модель", fontsize=10, loc='best') | |
# Общий заголовок | |
plt.suptitle('Зависимость macro_text_recall от подхода к чанкингу для разных моделей', fontsize=16) | |
# Настраиваем макет | |
plt.tight_layout(rect=[0, 0, 1, 0.96]) | |
# Сохраняем график | |
file_path = os.path.join(plots_dir, "chunking_vs_recall_by_model.png") | |
plt.savefig(file_path, dpi=DPI) | |
plt.close() | |
print(f"Создан график: {file_path}") | |
def main(): | |
"""Основная функция скрипта.""" | |
# Создаем директорию для графиков | |
setup_plots_directory(PLOTS_DIR) | |
# Загружаем данные | |
try: | |
macro_metrics = load_macro_metrics(EXCEL_FILE_PATH) | |
except Exception as e: | |
print(f"Критическая ошибка: {e}") | |
return | |
# Строим графики | |
plot_top_n_vs_recall_by_model(macro_metrics, PLOTS_DIR) | |
plot_top_n_vs_recall_by_chunking(macro_metrics, PLOTS_DIR) | |
plot_chunking_vs_recall_by_model(macro_metrics, PLOTS_DIR) | |
print("Готово! Все графики созданы.") | |
if __name__ == "__main__": | |
main() |