Spaces:
Runtime error
Runtime error
| #!/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() |