From 3e4815fcc618e2cda637a150281004fc61312225 Mon Sep 17 00:00:00 2001 From: Sergey Penkovsky Date: Tue, 14 Oct 2025 11:57:23 +0300 Subject: [PATCH] refactor(experiments): migrate to universal runner + config structure, remove legacy scripts - add universal runner run_llm_experiment.py with JSON-config driven LLM training / generation - add configs for gpt, gpt2, llama (training/generation) - remove individual train/generate scripts for each model - update README with simple how-to for experiments block BREAKING CHANGE: all llm_only experiments now run only through run_llm_experiment.py; legacy scripts removed --- README.md | 74 +++- .../llm_only/configs/gpt2_generate.json | 19 ++ experiments/llm_only/configs/gpt2_train.json | 23 ++ .../llm_only/configs/gpt_generate.json | 19 ++ experiments/llm_only/configs/gpt_train.json | 23 ++ .../llm_only/configs/llama_generate.json | 19 ++ experiments/llm_only/configs/llama_train.json | 23 ++ experiments/llm_only/generate_gpt2_bpe.py | 316 ------------------ experiments/llm_only/generate_gpt_bpe.py | 316 ------------------ experiments/llm_only/run_llm_experiment.py | 167 +++++++++ experiments/llm_only/train_gpt2_bpe.py | 237 ------------- experiments/llm_only/train_gpt_bpe.py | 237 ------------- experiments/llm_only/train_llama_bpe.py | 237 ------------- 13 files changed, 360 insertions(+), 1350 deletions(-) create mode 100644 experiments/llm_only/configs/gpt2_generate.json create mode 100644 experiments/llm_only/configs/gpt2_train.json create mode 100644 experiments/llm_only/configs/gpt_generate.json create mode 100644 experiments/llm_only/configs/gpt_train.json create mode 100644 experiments/llm_only/configs/llama_generate.json create mode 100644 experiments/llm_only/configs/llama_train.json delete mode 100644 experiments/llm_only/generate_gpt2_bpe.py delete mode 100644 experiments/llm_only/generate_gpt_bpe.py create mode 100644 experiments/llm_only/run_llm_experiment.py delete mode 100644 experiments/llm_only/train_gpt2_bpe.py delete mode 100644 experiments/llm_only/train_gpt_bpe.py delete mode 100644 experiments/llm_only/train_llama_bpe.py diff --git a/README.md b/README.md index 7a63cc3..cd099f8 100644 --- a/README.md +++ b/README.md @@ -91,15 +91,75 @@ uv sync uv sync --extra dev ``` -### Запуск обучения GPT +## ⚡ Работа с экспериментами (experiments/llm_only) -```bash -# Обучение базовой GPT модели -uv run python experiments/llm_only/train_gpt_bpe.py +В папке `experiments/llm_only` вы найдете универсальный скрипт для обучения и генерации LLM без HuggingFace. +Архитектура позволяет управлять выбором модели, типом действия и параметрами через аргументы командной строки и отдельные JSON-конфиги. -# Обучение с интеграцией HuggingFace -uv run python experiments/hf_integration/simple_hf_training.py -``` +### Основные файлы и директории + +- `run_llm_experiment.py` — универсальный скрипт-стартер для обучения и генерации. +- `configs/` — примеры конфигурационных файлов (`*.json`) для разных моделей и сценариев. + +### Использование универсального скрипта + +1. **Настройте конфиг** + Для каждой модели и режима работы есть отдельный JSON-файл с параметрами: + - `configs/gpt_train.json`, `configs/gpt_generate.json` + - `configs/gpt2_train.json`, `configs/gpt2_generate.json` + - `configs/llama_train.json`, `configs/llama_generate.json` + +2. **Запустите обучение или генерацию** + Стандартная команда: + + ```bash + python experiments/llm_only/run_llm_experiment.py --model <название_модели> --action --config experiments/llm_only/configs/<имя_конфига>.json + ``` + + Примеры: + + - Обучить GPT: + ```bash + python experiments/llm_only/run_llm_experiment.py --model gpt --action train --config experiments/llm_only/configs/gpt_train.json + ``` + + - Генерировать текст GPT2: + ```bash + python experiments/llm_only/run_llm_experiment.py --model gpt2 --action generate --config experiments/llm_only/configs/gpt2_generate.json + ``` + + - Обучить Llama: + ```bash + python experiments/llm_only/run_llm_experiment.py --model llama --action train --config experiments/llm_only/configs/llama_train.json + ``` + + - Генерировать текст Llama: + ```bash + python experiments/llm_only/run_llm_experiment.py --model llama --action generate --config experiments/llm_only/configs/llama_generate.json + ``` + +3. **Конфигурирование параметров** + - Все гиперпараметры (архитектура, обучение, генерация, пути) задаются в json-файле. + - Для новых моделей создайте копию существующего конфига, укажите другие веса и параметры, и используйте нужное название модели в команде. + +4. **Структура конфига** + Минимальный пример конфига для обучения: + ```json + { + "bpe_tokenizer": "checkpoints/bpe_tokenizer.json", + "model_config": { + "vocab_size": null, + "embed_dim": 256, + "num_heads": 4, + "num_layers": 4, + "max_position_embeddings": 128, + "dropout": 0.1 + }, + "model_weights": "checkpoints/gpt-bpe/model.pt" + } + ``` + +--- ### Тестирование hf-proxy diff --git a/experiments/llm_only/configs/gpt2_generate.json b/experiments/llm_only/configs/gpt2_generate.json new file mode 100644 index 0000000..bc04ee1 --- /dev/null +++ b/experiments/llm_only/configs/gpt2_generate.json @@ -0,0 +1,19 @@ +{ + "bpe_tokenizer": "checkpoints/bpe_tokenizer.json", + "test_prompts": [ + "Нейронные сети", + "Обработка естественного языка", + "GPT-2 — это" + ], + "model_config_path": "checkpoints/gpt2-bpe/config.json", + "model_weights": "checkpoints/gpt2-bpe/model.pt", + "generation": { + "max_new_tokens": 40, + "temperature": 0.8, + "do_sample": true, + "top_k": null, + "top_p": null + }, + "log_path": "checkpoints/llm_only_generation_logs.json" + } + \ No newline at end of file diff --git a/experiments/llm_only/configs/gpt2_train.json b/experiments/llm_only/configs/gpt2_train.json new file mode 100644 index 0000000..5f8f0be --- /dev/null +++ b/experiments/llm_only/configs/gpt2_train.json @@ -0,0 +1,23 @@ +{ + "bpe_tokenizer": "checkpoints/bpe_tokenizer.json", + "bpe_vocab_size": 1000, + "bpe_special_tokens": ["", "", "", ""], + "test_prompts": ["Искусственный интеллект", "Python — это"], + "model_config": { + "vocab_size": null, + "embed_dim": 256, + "num_heads": 4, + "num_layers": 4, + "max_position_embeddings": 128, + "dropout": 0.1 + }, + "model_weights": "checkpoints/gpt2-bpe/model.pt", + "model_config_path": "checkpoints/gpt2-bpe/config.json", + "training": { + "learning_rate": 0.0003, + "batch_size": 2, + "num_epochs": 3, + "warmup_steps": 50 + }, + "log_path": "checkpoints/gpt2_only_training_logs.json" + } \ No newline at end of file diff --git a/experiments/llm_only/configs/gpt_generate.json b/experiments/llm_only/configs/gpt_generate.json new file mode 100644 index 0000000..e049221 --- /dev/null +++ b/experiments/llm_only/configs/gpt_generate.json @@ -0,0 +1,19 @@ +{ + "bpe_tokenizer": "checkpoints/bpe_tokenizer.json", + "test_prompts": [ + "The neural network", + "Transformer architecture", + "GPT models are" + ], + "model_config_path": "checkpoints/gpt-bpe/config.json", + "model_weights": "checkpoints/gpt-bpe/model.pt", + "generation": { + "max_new_tokens": 40, + "temperature": 0.8, + "do_sample": true, + "top_k": null, + "top_p": null + }, + "log_path": "checkpoints/llm_only_generation_logs.json" + } + \ No newline at end of file diff --git a/experiments/llm_only/configs/gpt_train.json b/experiments/llm_only/configs/gpt_train.json new file mode 100644 index 0000000..f18707b --- /dev/null +++ b/experiments/llm_only/configs/gpt_train.json @@ -0,0 +1,23 @@ +{ + "bpe_tokenizer": "checkpoints/bpe_tokenizer.json", + "bpe_vocab_size": 1000, + "bpe_special_tokens": ["", "", "", ""], + "test_prompts": ["GPT language model", "Machine learning basics"], + "model_config": { + "vocab_size": null, + "embed_dim": 256, + "num_heads": 4, + "num_layers": 4, + "max_position_embeddings": 128, + "dropout": 0.1 + }, + "model_weights": "checkpoints/gpt-bpe/model.pt", + "model_config_path": "checkpoints/gpt-bpe/config.json", + "training": { + "learning_rate": 0.0003, + "batch_size": 2, + "num_epochs": 3, + "warmup_steps": 50 + }, + "log_path": "checkpoints/gpt_only_training_logs.json" + } \ No newline at end of file diff --git a/experiments/llm_only/configs/llama_generate.json b/experiments/llm_only/configs/llama_generate.json new file mode 100644 index 0000000..eccf688 --- /dev/null +++ b/experiments/llm_only/configs/llama_generate.json @@ -0,0 +1,19 @@ +{ + "bpe_tokenizer": "checkpoints/bpe_tokenizer.json", + "test_prompts": [ + "Open weights", + "The Llama model is", + "Efficient transformers" + ], + "model_config_path": "checkpoints/llama-bpe/config.json", + "model_weights": "checkpoints/llama-bpe/model.pt", + "generation": { + "max_new_tokens": 40, + "temperature": 0.8, + "do_sample": true, + "top_k": null, + "top_p": null + }, + "log_path": "checkpoints/llm_only_generation_logs.json" + } + \ No newline at end of file diff --git a/experiments/llm_only/configs/llama_train.json b/experiments/llm_only/configs/llama_train.json new file mode 100644 index 0000000..215401e --- /dev/null +++ b/experiments/llm_only/configs/llama_train.json @@ -0,0 +1,23 @@ +{ + "bpe_tokenizer": "checkpoints/bpe_tokenizer.json", + "bpe_vocab_size": 1000, + "bpe_special_tokens": ["", "", "", ""], + "test_prompts": ["Open source AI", "What is Llama?"], + "model_config": { + "vocab_size": null, + "embed_dim": 256, + "num_heads": 4, + "num_layers": 4, + "max_position_embeddings": 128, + "dropout": 0.1 + }, + "model_weights": "checkpoints/llama-bpe/model.pt", + "model_config_path": "checkpoints/llama-bpe/config.json", + "training": { + "learning_rate": 0.0003, + "batch_size": 2, + "num_epochs": 3, + "warmup_steps": 50 + }, + "log_path": "checkpoints/llama_only_training_logs.json" + } \ No newline at end of file diff --git a/experiments/llm_only/generate_gpt2_bpe.py b/experiments/llm_only/generate_gpt2_bpe.py deleted file mode 100644 index efd6368..0000000 --- a/experiments/llm_only/generate_gpt2_bpe.py +++ /dev/null @@ -1,316 +0,0 @@ -#!/usr/bin/env python3 -""" -Experiment: generate_gpt_bpe.py -Description: Генерация текста обученной GPT моделью с BPE токенизатором. -Использует только библиотеку llm без зависимостей от HuggingFace. -""" - -import torch -import os -import sys - -# Добавляем путь к shared модулям -sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from llm.models.gpt import GPT2 -from llm.tokenizers import BPETokenizer - -from shared.configs import BASE_GPT_CONFIG, TEST_PROMPTS, GENERATION_CONFIG, PATHS -from shared.data import print_experiment_info, ensure_directories, ExperimentLogger - - -def load_model_and_tokenizer() -> tuple: - """ - Загружает обученную модель и токенизатор. - - Returns: - tuple: (модель, токенизатор, конфигурация) - """ - # Проверяем существование файлов - if not os.path.exists(PATHS["gpt_bpe_model"]): - raise FileNotFoundError( - f"Модель не найдена: {PATHS['gpt_bpe_model']}\n" - f"Сначала обучите модель: uv run python experiments/llm_only/train_gpt_bpe.py" - ) - - if not os.path.exists(PATHS["bpe_tokenizer"]): - raise FileNotFoundError(f"Токенизатор не найден: {PATHS['bpe_tokenizer']}") - - # Загружаем конфигурацию модели - import json - - with open(PATHS["gpt_bpe_config"], "r", encoding="utf-8") as f: - model_config = json.load(f) - - # Загружаем токенизатор - print("🔧 Загрузка BPE токенизатора...") - tokenizer = BPETokenizer.load(PATHS["bpe_tokenizer"]) - print(f"✅ Токенизатор загружен (vocab_size={tokenizer.get_vocab_size()})") - - # Загружаем модель - print("🔧 Загрузка GPT2 модели...") - model = GPT2(model_config) - model.load_state_dict(torch.load(PATHS["gpt_bpe_model"], map_location="cpu")) - model.eval() - print("✅ Модель загружена") - - return model, tokenizer, model_config - - -def generate_text( - model: GPT2, tokenizer: BPETokenizer, prompt: str, config: dict -) -> str: - """ - Генерирует текст на основе промпта. - - Args: - model: Обученная GPT модель - tokenizer: BPE токенизатор - prompt: Входной текст - config: Конфигурация генерации - - Returns: - str: Сгенерированный текст - """ - print(f"🔤 Промпт: '{prompt}'") - print( - f"📊 Параметры: max_tokens={config['max_new_tokens']}, " - f"temp={config['temperature']}, sample={config['do_sample']}" - ) - - # Кодируем промпт - input_ids = tokenizer.encode(prompt, add_special_tokens=False) - input_tensor = torch.tensor([input_ids], dtype=torch.long) - - print(f"🎯 Токены промпта: {input_ids}") - print(f"🎯 Токены (текст): {tokenizer.tokenize(prompt)}") - print("🔄 Генерация...") - - # Генерируем текст - with torch.no_grad(): - generated_ids = model.generate( - x=input_tensor, - max_new_tokens=config["max_new_tokens"], - do_sample=config["do_sample"], - temperature=config["temperature"], - top_k=config["top_k"], - top_p=config["top_p"], - ) - - # Декодируем результат - generated_text = tokenizer.decode(generated_ids[0].tolist()) - - return generated_text - - -def test_different_strategies(model: GPT2, tokenizer: BPETokenizer, prompt: str): - """ - Тестирует разные стратегии генерации на одном промпте. - - Args: - model: Обученная модель - tokenizer: BPE токенизатор - prompt: Тестовый промпт - """ - print(f"\n🎭 Сравнение стратегий генерации для промпта: '{prompt}'") - print("=" * 60) - - strategies = [ - {"name": "🎯 Жадный поиск", "do_sample": False, "temperature": 1.0}, - {"name": "🎲 Вероятностная (temp=0.7)", "do_sample": True, "temperature": 0.7}, - {"name": "🔥 Случайная (temp=1.2)", "do_sample": True, "temperature": 1.2}, - { - "name": "❄️ Детерминированная (temp=0.3)", - "do_sample": True, - "temperature": 0.3, - }, - ] - - for strategy in strategies: - print(f"\n{strategy['name']}:") - try: - config = GENERATION_CONFIG.copy() - config.update( - { - "do_sample": strategy["do_sample"], - "temperature": strategy["temperature"], - "max_new_tokens": 20, - } - ) - - generated = generate_text(model, tokenizer, prompt, config) - - # Выделяем сгенерированную часть - generated_part = generated[len(prompt) :] - print(f" 📤 Промпт: '{prompt}'") - print(f" 🎯 Сгенерировано: '{generated_part}'") - print(f" 📄 Полный текст: '{generated}'") - - except Exception as e: - print(f" ❌ Ошибка: {e}") - - -def analyze_tokenization(tokenizer: BPETokenizer, texts: list): - """ - Анализирует токенизацию различных текстов. - - Args: - tokenizer: BPE токенизатор - texts: Список текстов для анализа - """ - print(f"\n🔍 Анализ токенизации BPE:") - print("=" * 50) - - for i, text in enumerate(texts): - print(f"\nТекст {i+1}: '{text}'") - - # Токенизация - tokens = tokenizer.encode(text, add_special_tokens=False) - token_strings = tokenizer.tokenize(text) - - print(f" Токены (ID): {tokens}") - print(f" Токены (текст): {token_strings}") - print(f" Количество токенов: {len(tokens)}") - print(f" Эффективность: {len(text)} символов → {len(tokens)} токенов") - - # Декодирование обратно - decoded = tokenizer.decode(tokens) - if text == decoded: - print(f" ✅ Декодирование корректно") - else: - print(f" ⚠️ Расхождения: '{decoded}'") - - -def interactive_generation(model: GPT2, tokenizer: BPETokenizer): - """ - Режим интерактивной генерации. - - Args: - model: Обученная модель - tokenizer: BPE токенизатор - """ - print(f"\n💬 Интерактивная генерация (для выхода введите 'exit')") - print("-" * 50) - - while True: - try: - user_input = input("\n🔤 Введите промпт: ").strip() - - if user_input.lower() in ["exit", "quit", "выход"]: - break - - if not user_input: - continue - - # Запрашиваем параметры - try: - max_tokens = int(input("📏 Макс. токенов [50]: ") or "50") - temperature = float(input("🌡️ Температура [0.7]: ") or "0.7") - do_sample_input = input("🎲 Сэмплирование (y/n) [y]: ").lower() - do_sample = do_sample_input != "n" - except: - max_tokens = 50 - temperature = 0.7 - do_sample = True - print("⚠️ Использую параметры по умолчанию") - - config = GENERATION_CONFIG.copy() - config.update( - { - "max_new_tokens": max_tokens, - "temperature": temperature, - "do_sample": do_sample, - } - ) - - generated = generate_text(model, tokenizer, user_input, config) - - generated_part = generated[len(user_input) :] - print(f"\n🎯 Результат:") - print(f" 📤 Промпт: '{user_input}'") - print(f" 🎯 Сгенерировано: '{generated_part}'") - print(f" 📄 Полный текст: '{generated}'") - - except KeyboardInterrupt: - print("\n👋 Завершение работы...") - break - except Exception as e: - print(f"❌ Ошибка: {e}") - - -def main(): - """Основная функция эксперимента.""" - # === Настройка эксперимента === - experiment_name = "Генерация текста GPT2 + BPE (только llm)" - experiment_config = { - "model": "GPT2 с BPE токенизатором", - "стратегия": "автономная генерация", - "вход": "промпты", - "выход": "сгенерированный текст", - } - - print_experiment_info(experiment_name, experiment_config) - ensure_directories() - logger = ExperimentLogger(experiment_name) - - try: - # Загружаем модель и токенизатор - model, tokenizer, model_config = load_model_and_tokenizer() - - # === Анализ токенизации === - analysis_texts = [ - "Искусственный интеллект", - "Нейронные сети", - "Машинное обучение", - ] - analyze_tokenization(tokenizer, analysis_texts) - - # === Генерация с разными промптами === - print(f"\n🎯 Генерация текста с разными промптами") - print("=" * 60) - - for i, prompt in enumerate(TEST_PROMPTS): - print(f"\n📝 Пример {i+1}/{len(TEST_PROMPTS)}") - print("-" * 40) - - try: - generated = generate_text(model, tokenizer, prompt, GENERATION_CONFIG) - - # Выделяем сгенерированную часть - generated_part = generated[len(prompt) :] - - print(f"📤 Промпт: '{prompt}'") - print(f"🎯 Сгенерировано: '{generated_part}'") - print(f"📄 Полный текст: '{generated}'") - print(f"📏 Длина: {len(generated)} символов") - - # Логируем успешную генерацию - logger.log_metric(f"generation_length_{i}", len(generated)) - - except Exception as e: - print(f"❌ Ошибка при генерации: {e}") - continue - - # === Сравнение стратегий генерации === - test_prompt = "Искусственный" - test_different_strategies(model, tokenizer, test_prompt) - - # === Интерактивная генерация === - interactive_generation(model, tokenizer) - - # === Сохранение результатов === - logger.save_logs("checkpoints/llm_only_generation_logs.json") - - print(f"\n🎉 Эксперимент генерации завершен успешно!") - - except FileNotFoundError as e: - print(f"❌ {e}") - except Exception as e: - print(f"❌ Ошибка в эксперименте: {e}") - import traceback - - traceback.print_exc() - - -if __name__ == "__main__": - main() diff --git a/experiments/llm_only/generate_gpt_bpe.py b/experiments/llm_only/generate_gpt_bpe.py deleted file mode 100644 index b74d401..0000000 --- a/experiments/llm_only/generate_gpt_bpe.py +++ /dev/null @@ -1,316 +0,0 @@ -#!/usr/bin/env python3 -""" -Experiment: generate_gpt_bpe.py -Description: Генерация текста обученной GPT моделью с BPE токенизатором. -Использует только библиотеку llm без зависимостей от HuggingFace. -""" - -import torch -import os -import sys - -# Добавляем путь к shared модулям -sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from llm.models.gpt import GPT -from llm.tokenizers import BPETokenizer - -from shared.configs import BASE_GPT_CONFIG, TEST_PROMPTS, GENERATION_CONFIG, PATHS -from shared.data import print_experiment_info, ensure_directories, ExperimentLogger - - -def load_model_and_tokenizer() -> tuple: - """ - Загружает обученную модель и токенизатор. - - Returns: - tuple: (модель, токенизатор, конфигурация) - """ - # Проверяем существование файлов - if not os.path.exists(PATHS["gpt_bpe_model"]): - raise FileNotFoundError( - f"Модель не найдена: {PATHS['gpt_bpe_model']}\n" - f"Сначала обучите модель: uv run python experiments/llm_only/train_gpt_bpe.py" - ) - - if not os.path.exists(PATHS["bpe_tokenizer"]): - raise FileNotFoundError(f"Токенизатор не найден: {PATHS['bpe_tokenizer']}") - - # Загружаем конфигурацию модели - import json - - with open(PATHS["gpt_bpe_config"], "r", encoding="utf-8") as f: - model_config = json.load(f) - - # Загружаем токенизатор - print("🔧 Загрузка BPE токенизатора...") - tokenizer = BPETokenizer.load(PATHS["bpe_tokenizer"]) - print(f"✅ Токенизатор загружен (vocab_size={tokenizer.get_vocab_size()})") - - # Загружаем модель - print("🔧 Загрузка GPT модели...") - model = GPT(model_config) - model.load_state_dict(torch.load(PATHS["gpt_bpe_model"], map_location="cpu")) - model.eval() - print("✅ Модель загружена") - - return model, tokenizer, model_config - - -def generate_text( - model: GPT, tokenizer: BPETokenizer, prompt: str, config: dict -) -> str: - """ - Генерирует текст на основе промпта. - - Args: - model: Обученная GPT модель - tokenizer: BPE токенизатор - prompt: Входной текст - config: Конфигурация генерации - - Returns: - str: Сгенерированный текст - """ - print(f"🔤 Промпт: '{prompt}'") - print( - f"📊 Параметры: max_tokens={config['max_new_tokens']}, " - f"temp={config['temperature']}, sample={config['do_sample']}" - ) - - # Кодируем промпт - input_ids = tokenizer.encode(prompt, add_special_tokens=False) - input_tensor = torch.tensor([input_ids], dtype=torch.long) - - print(f"🎯 Токены промпта: {input_ids}") - print(f"🎯 Токены (текст): {tokenizer.tokenize(prompt)}") - print("🔄 Генерация...") - - # Генерируем текст - with torch.no_grad(): - generated_ids = model.generate( - x=input_tensor, - max_new_tokens=config["max_new_tokens"], - do_sample=config["do_sample"], - temperature=config["temperature"], - top_k=config["top_k"], - top_p=config["top_p"], - ) - - # Декодируем результат - generated_text = tokenizer.decode(generated_ids[0].tolist()) - - return generated_text - - -def test_different_strategies(model: GPT, tokenizer: BPETokenizer, prompt: str): - """ - Тестирует разные стратегии генерации на одном промпте. - - Args: - model: Обученная модель - tokenizer: BPE токенизатор - prompt: Тестовый промпт - """ - print(f"\n🎭 Сравнение стратегий генерации для промпта: '{prompt}'") - print("=" * 60) - - strategies = [ - {"name": "🎯 Жадный поиск", "do_sample": False, "temperature": 1.0}, - {"name": "🎲 Вероятностная (temp=0.7)", "do_sample": True, "temperature": 0.7}, - {"name": "🔥 Случайная (temp=1.2)", "do_sample": True, "temperature": 1.2}, - { - "name": "❄️ Детерминированная (temp=0.3)", - "do_sample": True, - "temperature": 0.3, - }, - ] - - for strategy in strategies: - print(f"\n{strategy['name']}:") - try: - config = GENERATION_CONFIG.copy() - config.update( - { - "do_sample": strategy["do_sample"], - "temperature": strategy["temperature"], - "max_new_tokens": 20, - } - ) - - generated = generate_text(model, tokenizer, prompt, config) - - # Выделяем сгенерированную часть - generated_part = generated[len(prompt) :] - print(f" 📤 Промпт: '{prompt}'") - print(f" 🎯 Сгенерировано: '{generated_part}'") - print(f" 📄 Полный текст: '{generated}'") - - except Exception as e: - print(f" ❌ Ошибка: {e}") - - -def analyze_tokenization(tokenizer: BPETokenizer, texts: list): - """ - Анализирует токенизацию различных текстов. - - Args: - tokenizer: BPE токенизатор - texts: Список текстов для анализа - """ - print(f"\n🔍 Анализ токенизации BPE:") - print("=" * 50) - - for i, text in enumerate(texts): - print(f"\nТекст {i+1}: '{text}'") - - # Токенизация - tokens = tokenizer.encode(text, add_special_tokens=False) - token_strings = tokenizer.tokenize(text) - - print(f" Токены (ID): {tokens}") - print(f" Токены (текст): {token_strings}") - print(f" Количество токенов: {len(tokens)}") - print(f" Эффективность: {len(text)} символов → {len(tokens)} токенов") - - # Декодирование обратно - decoded = tokenizer.decode(tokens) - if text == decoded: - print(f" ✅ Декодирование корректно") - else: - print(f" ⚠️ Расхождения: '{decoded}'") - - -def interactive_generation(model: GPT, tokenizer: BPETokenizer): - """ - Режим интерактивной генерации. - - Args: - model: Обученная модель - tokenizer: BPE токенизатор - """ - print(f"\n💬 Интерактивная генерация (для выхода введите 'exit')") - print("-" * 50) - - while True: - try: - user_input = input("\n🔤 Введите промпт: ").strip() - - if user_input.lower() in ["exit", "quit", "выход"]: - break - - if not user_input: - continue - - # Запрашиваем параметры - try: - max_tokens = int(input("📏 Макс. токенов [50]: ") or "50") - temperature = float(input("🌡️ Температура [0.7]: ") or "0.7") - do_sample_input = input("🎲 Сэмплирование (y/n) [y]: ").lower() - do_sample = do_sample_input != "n" - except: - max_tokens = 50 - temperature = 0.7 - do_sample = True - print("⚠️ Использую параметры по умолчанию") - - config = GENERATION_CONFIG.copy() - config.update( - { - "max_new_tokens": max_tokens, - "temperature": temperature, - "do_sample": do_sample, - } - ) - - generated = generate_text(model, tokenizer, user_input, config) - - generated_part = generated[len(user_input) :] - print(f"\n🎯 Результат:") - print(f" 📤 Промпт: '{user_input}'") - print(f" 🎯 Сгенерировано: '{generated_part}'") - print(f" 📄 Полный текст: '{generated}'") - - except KeyboardInterrupt: - print("\n👋 Завершение работы...") - break - except Exception as e: - print(f"❌ Ошибка: {e}") - - -def main(): - """Основная функция эксперимента.""" - # === Настройка эксперимента === - experiment_name = "Генерация текста GPT + BPE (только llm)" - experiment_config = { - "model": "GPT с BPE токенизатором", - "стратегия": "автономная генерация", - "вход": "промпты", - "выход": "сгенерированный текст", - } - - print_experiment_info(experiment_name, experiment_config) - ensure_directories() - logger = ExperimentLogger(experiment_name) - - try: - # Загружаем модель и токенизатор - model, tokenizer, model_config = load_model_and_tokenizer() - - # === Анализ токенизации === - analysis_texts = [ - "Искусственный интеллект", - "Нейронные сети", - "Машинное обучение", - ] - analyze_tokenization(tokenizer, analysis_texts) - - # === Генерация с разными промптами === - print(f"\n🎯 Генерация текста с разными промптами") - print("=" * 60) - - for i, prompt in enumerate(TEST_PROMPTS): - print(f"\n📝 Пример {i+1}/{len(TEST_PROMPTS)}") - print("-" * 40) - - try: - generated = generate_text(model, tokenizer, prompt, GENERATION_CONFIG) - - # Выделяем сгенерированную часть - generated_part = generated[len(prompt) :] - - print(f"📤 Промпт: '{prompt}'") - print(f"🎯 Сгенерировано: '{generated_part}'") - print(f"📄 Полный текст: '{generated}'") - print(f"📏 Длина: {len(generated)} символов") - - # Логируем успешную генерацию - logger.log_metric(f"generation_length_{i}", len(generated)) - - except Exception as e: - print(f"❌ Ошибка при генерации: {e}") - continue - - # === Сравнение стратегий генерации === - test_prompt = "Искусственный" - test_different_strategies(model, tokenizer, test_prompt) - - # === Интерактивная генерация === - interactive_generation(model, tokenizer) - - # === Сохранение результатов === - logger.save_logs("checkpoints/llm_only_generation_logs.json") - - print(f"\n🎉 Эксперимент генерации завершен успешно!") - - except FileNotFoundError as e: - print(f"❌ {e}") - except Exception as e: - print(f"❌ Ошибка в эксперименте: {e}") - import traceback - - traceback.print_exc() - - -if __name__ == "__main__": - main() diff --git a/experiments/llm_only/run_llm_experiment.py b/experiments/llm_only/run_llm_experiment.py new file mode 100644 index 0000000..5743785 --- /dev/null +++ b/experiments/llm_only/run_llm_experiment.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python3 +""" +Универсальный скрипт для обучения и генерации LLM. +Позволяет выбирать тип модели и действие через аргументы, +а специальные параметры подавать отдельным JSON-конфигом. +""" + +import argparse +import json +import os +import sys + +import torch + +# Добавляем директорию shared среди импортируемых +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from llm.tokenizers import BPETokenizer +from llm.training.dataset import TextDataset +from llm.training.trainer import Trainer + +from shared.data import ( + print_experiment_info, + ensure_directories, + load_training_data, + ExperimentLogger, +) + + +def load_config(config_path): + with open(config_path, "r", encoding="utf-8") as f: + return json.load(f) + + +def load_model_class(model_name): + if model_name.lower() == 'gpt': + from llm.models.gpt import GPT + return GPT + elif model_name.lower() == 'gpt2': + from llm.models.gpt import GPT2 + return GPT2 + elif model_name.lower() == 'llama': + from llm.models.llama import Llama + return Llama + else: + raise ValueError(f"Модель '{model_name}' не поддерживается.") + + +def main(): + parser = argparse.ArgumentParser(description='Универсальный запуск обучения/генерации LLM.') + parser.add_argument('--model', '-m', type=str, required=True, help='Название модели (gpt, gpt2, llama и т.д.).') + parser.add_argument('--action', '-a', type=str, required=True, choices=['train', 'generate'], help='Действие: train или generate.') + parser.add_argument('--config', '-c', type=str, required=True, help='Путь к JSON-конфигу с параметрами.') + args = parser.parse_args() + + config = load_config(args.config) + ModelClass = load_model_class(args.model) + logger = ExperimentLogger(f"{args.action}_{args.model}") + + print_experiment_info(f"Эксперимент {args.action} {args.model}", config) + ensure_directories() + + # ==== Обучение ==== + if args.action == 'train': + train_texts, val_texts = load_training_data() + # --- Токенизатор --- + if os.path.exists(config["bpe_tokenizer"]): + print("📝 Загрузка обученного токенизатора...") + tokenizer = BPETokenizer.load(config["bpe_tokenizer"]) + print(f"✅ Токенизатор загружен (vocab_size={tokenizer.get_vocab_size()})") + else: + print("🔧 Обучение BPE токенизатора...") + tokenizer = BPETokenizer() + tokenizer.train( + texts=train_texts, + vocab_size=config["bpe_vocab_size"], + special_tokens=config["bpe_special_tokens"] + ) + os.makedirs(os.path.dirname(config["bpe_tokenizer"]), exist_ok=True) + tokenizer.save(config["bpe_tokenizer"]) + print(f"✅ BPE токенизатор обучен и сохранен: {config['bpe_tokenizer']}") + + # Тестируем токенизатор (базово) + for test_text in config.get("test_prompts", ["Тест"]): + encoded = tokenizer.encode(test_text) + decoded = tokenizer.decode(encoded) + print(f"[TEST TOK] '{test_text}' → {encoded} → '{decoded}'") + + # --- Модель --- + model_config = config["model_config"] + model_config["vocab_size"] = tokenizer.get_vocab_size() + model = ModelClass(model_config) + + # --- Датасет --- + train_dataset = TextDataset( + train_texts, + tokenizer, + block_size=model_config["max_position_embeddings"] + ) + print(f" Размер train датасета: {len(train_dataset)} примеров") + + # --- Trainer --- + training = config["training"] + trainer = Trainer( + model=model, + train_dataset=train_dataset, + lr=training["learning_rate"], + batch_size=training["batch_size"], + num_epochs=training["num_epochs"], + warmup_steps=training.get("warmup_steps", 0), + ) + trainer.train() + + # --- Сохранение модели --- + os.makedirs(os.path.dirname(config["model_weights"]), exist_ok=True) + torch.save(model.state_dict(), config["model_weights"]) + with open(config["model_config_path"], "w", encoding="utf-8") as f: + json.dump(model_config, f, indent=2, ensure_ascii=False) + print(f"✅ Модель сохранена: {config['model_weights']}") + + logger.save_logs(config.get("log_path", "checkpoints/llm_only_training_logs.json")) + + # ==== Генерация ==== + elif args.action == 'generate': + # --- Загрузка --- + if not os.path.exists(config["model_weights"]): + raise FileNotFoundError(f"Модель не найдена: {config['model_weights']}") + if not os.path.exists(config["bpe_tokenizer"]): + raise FileNotFoundError(f"Токенизатор не найден: {config['bpe_tokenizer']}") + with open(config["model_config_path"], "r", encoding="utf-8") as f: + model_config = json.load(f) + tokenizer = BPETokenizer.load(config["bpe_tokenizer"]) + model = ModelClass(model_config) + model.load_state_dict(torch.load(config["model_weights"], map_location="cpu")) + model.eval() + + def generate(prompt, gen_cfg): + print(f"Промпт: {prompt}") + input_ids = tokenizer.encode(prompt, add_special_tokens=False) + input_tensor = torch.tensor([input_ids], dtype=torch.long) + with torch.no_grad(): + generated_ids = model.generate( + x=input_tensor, + max_new_tokens=gen_cfg["max_new_tokens"], + do_sample=gen_cfg["do_sample"], + temperature=gen_cfg["temperature"], + top_k=gen_cfg.get("top_k"), + top_p=gen_cfg.get("top_p"), + ) + return tokenizer.decode(generated_ids[0].tolist()) + + prompts = config.get("test_prompts", ["Тестовый промпт"]) + gen_cfg = config.get("generation", { + "max_new_tokens": 50, + "temperature": 0.7, + "do_sample": True, + "top_k": None, + "top_p": None + }) + for prompt in prompts: + generated = generate(prompt, gen_cfg) + print(f"\n[RESULT] Prompt: '{prompt}'\n---\n{generated}\n{'='*60}") + + logger.save_logs(config.get("log_path", "checkpoints/llm_only_generation_logs.json")) + +if __name__ == "__main__": + main() diff --git a/experiments/llm_only/train_gpt2_bpe.py b/experiments/llm_only/train_gpt2_bpe.py deleted file mode 100644 index b6aa703..0000000 --- a/experiments/llm_only/train_gpt2_bpe.py +++ /dev/null @@ -1,237 +0,0 @@ -#!/usr/bin/env python3 -""" -Experiment: train_gpt_bpe.py -Description: Обучение GPT модели с собственным BPE токенизатором. -Использует только библиотеку llm без зависимостей от HuggingFace. -""" - -import torch -import os -import sys - -# Добавляем путь к shared модулям -sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from llm.models.gpt import GPT2 -from llm.tokenizers import BPETokenizer -from llm.training.dataset import TextDataset -from llm.training.trainer import Trainer - -from shared.configs import ( - TRAIN_TEXTS, - BASE_GPT_CONFIG, - BPE_CONFIG, - TRAINING_CONFIG, - PATHS, - TEST_PROMPTS, -) -from shared.data import ( - load_training_data, - ensure_directories, - print_experiment_info, - ExperimentLogger, -) - - -def train_bpe_tokenizer(texts: list, config: dict) -> BPETokenizer: - """ - Обучает BPE токенизатор на текстах. - - Args: - texts: Список текстов для обучения - config: Конфигурация токенизатора - - Returns: - BPETokenizer: Обученный токенизатор - """ - print("🔧 Обучение BPE токенизатора...") - - tokenizer = BPETokenizer() - tokenizer.train( - texts=texts, - vocab_size=config["vocab_size"], - special_tokens=config["special_tokens"], - ) - - # Сохраняем токенизатор - os.makedirs(os.path.dirname(PATHS["bpe_tokenizer"]), exist_ok=True) - tokenizer.save(PATHS["bpe_tokenizer"]) - - print(f"✅ BPE токенизатор обучен и сохранен: {PATHS['bpe_tokenizer']}") - print(f"📊 Размер словаря: {tokenizer.get_vocab_size()}") - - return tokenizer - - -def test_tokenizer(tokenizer: BPETokenizer, texts: list): - """ - Тестирует токенизатор на примерах. - - Args: - tokenizer: Обученный токенизатор - texts: Список тестовых текстов - """ - print("\n🧪 Тестирование токенизатора:") - - for i, text in enumerate(texts[:3]): - print(f"\nПример {i+1}:") - print(f" Исходный текст: '{text}'") - - # Кодирование - tokens = tokenizer.encode(text) - token_strings = tokenizer.tokenize(text) - - print(f" Токены (ID): {tokens}") - print(f" Токены (текст): {token_strings}") - print(f" Количество токенов: {len(tokens)}") - - # Декодирование - decoded = tokenizer.decode(tokens) - print(f" Декодированный: '{decoded}'") - - if text == decoded: - print(" ✅ Кодирование/декодирование корректно") - else: - print(" ⚠️ Небольшие расхождения") - - -def main(): - """Основная функция эксперимента.""" - # === Настройка эксперимента === - experiment_name = "Обучение GPT2 с BPE токенизатором (только llm)" - experiment_config = { - "model": "GPT2", - "tokenizer": "BPE", - "vocab_size": BPE_CONFIG["vocab_size"], - "training_epochs": TRAINING_CONFIG["num_epochs"], - "batch_size": TRAINING_CONFIG["batch_size"], - "learning_rate": TRAINING_CONFIG["learning_rate"], - } - - print_experiment_info(experiment_name, experiment_config) - ensure_directories() - logger = ExperimentLogger(experiment_name) - - try: - # === Подготовка данных === - train_texts, val_texts = load_training_data() - print(f"📊 Данные: {len(train_texts)} train, {len(val_texts)} validation") - - # === Обучение токенизатора === - if os.path.exists(PATHS["bpe_tokenizer"]): - print("📝 Загрузка предварительно обученного токенизатора...") - tokenizer = BPETokenizer.load(PATHS["bpe_tokenizer"]) - print(f"✅ Токенизатор загружен (vocab_size={tokenizer.get_vocab_size()})") - else: - tokenizer = train_bpe_tokenizer(TRAIN_TEXTS, BPE_CONFIG) - - # Тестируем токенизатор - test_tokenizer(tokenizer, TEST_PROMPTS[:3]) - - # === Инициализация модели === - model_config = BASE_GPT_CONFIG.copy() - model_config["vocab_size"] = tokenizer.get_vocab_size() - - print(f"\n🔧 Инициализация GPT2 модели...") - print(f" Размер словаря: {model_config['vocab_size']}") - print(f" Размер эмбеддингов: {model_config['embed_dim']}") - print(f" Количество слоев: {model_config['num_layers']}") - print(f" Количество голов внимания: {model_config['num_heads']}") - - model = GPT2(model_config) - - # === Подготовка датасета === - print(f"\n📊 Подготовка датасета...") - train_dataset = TextDataset( - train_texts, tokenizer, block_size=model_config["max_position_embeddings"] - ) - print(f" Размер train датасета: {len(train_dataset)} примеров") - - # === Обучение модели === - print(f"\n🎯 Начало обучения GPT2 модели...") - - trainer = Trainer( - model=model, - train_dataset=train_dataset, - lr=TRAINING_CONFIG["learning_rate"], - batch_size=TRAINING_CONFIG["batch_size"], - num_epochs=TRAINING_CONFIG["num_epochs"], - warmup_steps=TRAINING_CONFIG["warmup_steps"], - ) - - # Запускаем обучение - trainer.train() - - # === Сохранение модели === - print(f"\n💾 Сохранение модели...") - os.makedirs(os.path.dirname(PATHS["gpt_bpe_model"]), exist_ok=True) - - # Сохраняем модель - torch.save(model.state_dict(), PATHS["gpt_bpe_model"]) - - # Сохраняем конфигурацию - import json - - with open(PATHS["gpt_bpe_config"], "w", encoding="utf-8") as f: - json.dump(model_config, f, indent=2, ensure_ascii=False) - - print(f"✅ Модель сохранена:") - print(f" - {PATHS['gpt_bpe_model']}: веса модели") - print(f" - {PATHS['gpt_bpe_config']}: конфигурация модели") - print(f" - {PATHS['bpe_tokenizer']}: токенизатор") - - # === Тестирование генерации === - print(f"\n🧪 Тестирование генерации текста...") - model.eval() - - for prompt in TEST_PROMPTS[:3]: - print(f"\n🔤 Промпт: '{prompt}'") - - try: - # Кодируем промпт - input_ids = tokenizer.encode(prompt, add_special_tokens=False) - input_tensor = torch.tensor([input_ids], dtype=torch.long) - - # Генерируем текст - with torch.no_grad(): - generated_ids = model.generate( - x=input_tensor, - max_new_tokens=20, - do_sample=True, - temperature=0.8, - ) - - # Декодируем результат - generated_text = tokenizer.decode(generated_ids[0].tolist()) - generated_part = generated_text[len(prompt) :] - - print(f"🎯 Сгенерировано: '{generated_part}'") - print(f"📄 Полный текст: '{generated_text}'") - - except Exception as e: - print(f"❌ Ошибка генерации: {e}") - - # === Сохранение результатов === - results = { - "experiment": experiment_name, - "model_config": model_config, - "training_config": TRAINING_CONFIG, - "tokenizer_vocab_size": tokenizer.get_vocab_size(), - "final_loss": "см. логи обучения", # В реальном эксперименте можно сохранить final loss - } - - logger.save_logs("checkpoints/llm_only_training_logs.json") - - print(f"\n🎉 Эксперимент завершен успешно!") - print(f"\n💡 Для использования обученной модели:") - print(f" uv run python experiments/llm_only/generate_gpt_bpe.py") - - except Exception as e: - print(f"❌ Ошибка в эксперименте: {e}") - import traceback - - traceback.print_exc() - - -if __name__ == "__main__": - main() diff --git a/experiments/llm_only/train_gpt_bpe.py b/experiments/llm_only/train_gpt_bpe.py deleted file mode 100644 index 42355f8..0000000 --- a/experiments/llm_only/train_gpt_bpe.py +++ /dev/null @@ -1,237 +0,0 @@ -#!/usr/bin/env python3 -""" -Experiment: train_gpt_bpe.py -Description: Обучение GPT модели с собственным BPE токенизатором. -Использует только библиотеку llm без зависимостей от HuggingFace. -""" - -import torch -import os -import sys - -# Добавляем путь к shared модулям -sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from llm.models.gpt import GPT -from llm.tokenizers import BPETokenizer -from llm.training.dataset import TextDataset -from llm.training.trainer import Trainer - -from shared.configs import ( - TRAIN_TEXTS, - BASE_GPT_CONFIG, - BPE_CONFIG, - TRAINING_CONFIG, - PATHS, - TEST_PROMPTS, -) -from shared.data import ( - load_training_data, - ensure_directories, - print_experiment_info, - ExperimentLogger, -) - - -def train_bpe_tokenizer(texts: list, config: dict) -> BPETokenizer: - """ - Обучает BPE токенизатор на текстах. - - Args: - texts: Список текстов для обучения - config: Конфигурация токенизатора - - Returns: - BPETokenizer: Обученный токенизатор - """ - print("🔧 Обучение BPE токенизатора...") - - tokenizer = BPETokenizer() - tokenizer.train( - texts=texts, - vocab_size=config["vocab_size"], - special_tokens=config["special_tokens"], - ) - - # Сохраняем токенизатор - os.makedirs(os.path.dirname(PATHS["bpe_tokenizer"]), exist_ok=True) - tokenizer.save(PATHS["bpe_tokenizer"]) - - print(f"✅ BPE токенизатор обучен и сохранен: {PATHS['bpe_tokenizer']}") - print(f"📊 Размер словаря: {tokenizer.get_vocab_size()}") - - return tokenizer - - -def test_tokenizer(tokenizer: BPETokenizer, texts: list): - """ - Тестирует токенизатор на примерах. - - Args: - tokenizer: Обученный токенизатор - texts: Список тестовых текстов - """ - print("\n🧪 Тестирование токенизатора:") - - for i, text in enumerate(texts[:3]): - print(f"\nПример {i+1}:") - print(f" Исходный текст: '{text}'") - - # Кодирование - tokens = tokenizer.encode(text) - token_strings = tokenizer.tokenize(text) - - print(f" Токены (ID): {tokens}") - print(f" Токены (текст): {token_strings}") - print(f" Количество токенов: {len(tokens)}") - - # Декодирование - decoded = tokenizer.decode(tokens) - print(f" Декодированный: '{decoded}'") - - if text == decoded: - print(" ✅ Кодирование/декодирование корректно") - else: - print(" ⚠️ Небольшие расхождения") - - -def main(): - """Основная функция эксперимента.""" - # === Настройка эксперимента === - experiment_name = "Обучение GPT с BPE токенизатором (только llm)" - experiment_config = { - "model": "GPT", - "tokenizer": "BPE", - "vocab_size": BPE_CONFIG["vocab_size"], - "training_epochs": TRAINING_CONFIG["num_epochs"], - "batch_size": TRAINING_CONFIG["batch_size"], - "learning_rate": TRAINING_CONFIG["learning_rate"], - } - - print_experiment_info(experiment_name, experiment_config) - ensure_directories() - logger = ExperimentLogger(experiment_name) - - try: - # === Подготовка данных === - train_texts, val_texts = load_training_data() - print(f"📊 Данные: {len(train_texts)} train, {len(val_texts)} validation") - - # === Обучение токенизатора === - if os.path.exists(PATHS["bpe_tokenizer"]): - print("📝 Загрузка предварительно обученного токенизатора...") - tokenizer = BPETokenizer.load(PATHS["bpe_tokenizer"]) - print(f"✅ Токенизатор загружен (vocab_size={tokenizer.get_vocab_size()})") - else: - tokenizer = train_bpe_tokenizer(TRAIN_TEXTS, BPE_CONFIG) - - # Тестируем токенизатор - test_tokenizer(tokenizer, TEST_PROMPTS[:3]) - - # === Инициализация модели === - model_config = BASE_GPT_CONFIG.copy() - model_config["vocab_size"] = tokenizer.get_vocab_size() - - print(f"\n🔧 Инициализация GPT модели...") - print(f" Размер словаря: {model_config['vocab_size']}") - print(f" Размер эмбеддингов: {model_config['embed_dim']}") - print(f" Количество слоев: {model_config['num_layers']}") - print(f" Количество голов внимания: {model_config['num_heads']}") - - model = GPT(model_config) - - # === Подготовка датасета === - print(f"\n📊 Подготовка датасета...") - train_dataset = TextDataset( - train_texts, tokenizer, block_size=model_config["max_position_embeddings"] - ) - print(f" Размер train датасета: {len(train_dataset)} примеров") - - # === Обучение модели === - print(f"\n🎯 Начало обучения GPT модели...") - - trainer = Trainer( - model=model, - train_dataset=train_dataset, - lr=TRAINING_CONFIG["learning_rate"], - batch_size=TRAINING_CONFIG["batch_size"], - num_epochs=TRAINING_CONFIG["num_epochs"], - warmup_steps=TRAINING_CONFIG["warmup_steps"], - ) - - # Запускаем обучение - trainer.train() - - # === Сохранение модели === - print(f"\n💾 Сохранение модели...") - os.makedirs(os.path.dirname(PATHS["gpt_bpe_model"]), exist_ok=True) - - # Сохраняем модель - torch.save(model.state_dict(), PATHS["gpt_bpe_model"]) - - # Сохраняем конфигурацию - import json - - with open(PATHS["gpt_bpe_config"], "w", encoding="utf-8") as f: - json.dump(model_config, f, indent=2, ensure_ascii=False) - - print(f"✅ Модель сохранена:") - print(f" - {PATHS['gpt_bpe_model']}: веса модели") - print(f" - {PATHS['gpt_bpe_config']}: конфигурация модели") - print(f" - {PATHS['bpe_tokenizer']}: токенизатор") - - # === Тестирование генерации === - print(f"\n🧪 Тестирование генерации текста...") - model.eval() - - for prompt in TEST_PROMPTS[:3]: - print(f"\n🔤 Промпт: '{prompt}'") - - try: - # Кодируем промпт - input_ids = tokenizer.encode(prompt, add_special_tokens=False) - input_tensor = torch.tensor([input_ids], dtype=torch.long) - - # Генерируем текст - with torch.no_grad(): - generated_ids = model.generate( - x=input_tensor, - max_new_tokens=20, - do_sample=True, - temperature=0.8, - ) - - # Декодируем результат - generated_text = tokenizer.decode(generated_ids[0].tolist()) - generated_part = generated_text[len(prompt) :] - - print(f"🎯 Сгенерировано: '{generated_part}'") - print(f"📄 Полный текст: '{generated_text}'") - - except Exception as e: - print(f"❌ Ошибка генерации: {e}") - - # === Сохранение результатов === - results = { - "experiment": experiment_name, - "model_config": model_config, - "training_config": TRAINING_CONFIG, - "tokenizer_vocab_size": tokenizer.get_vocab_size(), - "final_loss": "см. логи обучения", # В реальном эксперименте можно сохранить final loss - } - - logger.save_logs("checkpoints/llm_only_training_logs.json") - - print(f"\n🎉 Эксперимент завершен успешно!") - print(f"\n💡 Для использования обученной модели:") - print(f" uv run python experiments/llm_only/generate_gpt_bpe.py") - - except Exception as e: - print(f"❌ Ошибка в эксперименте: {e}") - import traceback - - traceback.print_exc() - - -if __name__ == "__main__": - main() diff --git a/experiments/llm_only/train_llama_bpe.py b/experiments/llm_only/train_llama_bpe.py deleted file mode 100644 index 4c609ac..0000000 --- a/experiments/llm_only/train_llama_bpe.py +++ /dev/null @@ -1,237 +0,0 @@ -#!/usr/bin/env python3 -""" -Experiment: train_gpt_bpe.py -Description: Обучение GPT модели с собственным BPE токенизатором. -Использует только библиотеку llm без зависимостей от HuggingFace. -""" - -import torch -import os -import sys - -# Добавляем путь к shared модулям -sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from llm.models.llama import Llama -from llm.tokenizers import BPETokenizer -from llm.training.dataset import TextDataset -from llm.training.trainer import Trainer - -from shared.configs import ( - TRAIN_TEXTS, - BASE_GPT_CONFIG, - BPE_CONFIG, - TRAINING_CONFIG, - PATHS, - TEST_PROMPTS, -) -from shared.data import ( - load_training_data, - ensure_directories, - print_experiment_info, - ExperimentLogger, -) - - -def train_bpe_tokenizer(texts: list, config: dict) -> BPETokenizer: - """ - Обучает BPE токенизатор на текстах. - - Args: - texts: Список текстов для обучения - config: Конфигурация токенизатора - - Returns: - BPETokenizer: Обученный токенизатор - """ - print("🔧 Обучение BPE токенизатора...") - - tokenizer = BPETokenizer() - tokenizer.train( - texts=texts, - vocab_size=config["vocab_size"], - special_tokens=config["special_tokens"], - ) - - # Сохраняем токенизатор - os.makedirs(os.path.dirname(PATHS["bpe_tokenizer"]), exist_ok=True) - tokenizer.save(PATHS["bpe_tokenizer"]) - - print(f"✅ BPE токенизатор обучен и сохранен: {PATHS['bpe_tokenizer']}") - print(f"📊 Размер словаря: {tokenizer.get_vocab_size()}") - - return tokenizer - - -def test_tokenizer(tokenizer: BPETokenizer, texts: list): - """ - Тестирует токенизатор на примерах. - - Args: - tokenizer: Обученный токенизатор - texts: Список тестовых текстов - """ - print("\n🧪 Тестирование токенизатора:") - - for i, text in enumerate(texts[:3]): - print(f"\nПример {i+1}:") - print(f" Исходный текст: '{text}'") - - # Кодирование - tokens = tokenizer.encode(text) - token_strings = tokenizer.tokenize(text) - - print(f" Токены (ID): {tokens}") - print(f" Токены (текст): {token_strings}") - print(f" Количество токенов: {len(tokens)}") - - # Декодирование - decoded = tokenizer.decode(tokens) - print(f" Декодированный: '{decoded}'") - - if text == decoded: - print(" ✅ Кодирование/декодирование корректно") - else: - print(" ⚠️ Небольшие расхождения") - - -def main(): - """Основная функция эксперимента.""" - # === Настройка эксперимента === - experiment_name = "Обучение Llama с BPE токенизатором (только llm)" - experiment_config = { - "model": "Llama", - "tokenizer": "BPE", - "vocab_size": BPE_CONFIG["vocab_size"], - "training_epochs": TRAINING_CONFIG["num_epochs"], - "batch_size": TRAINING_CONFIG["batch_size"], - "learning_rate": TRAINING_CONFIG["learning_rate"], - } - - print_experiment_info(experiment_name, experiment_config) - ensure_directories() - logger = ExperimentLogger(experiment_name) - - try: - # === Подготовка данных === - train_texts, val_texts = load_training_data() - print(f"📊 Данные: {len(train_texts)} train, {len(val_texts)} validation") - - # === Обучение токенизатора === - if os.path.exists(PATHS["bpe_tokenizer"]): - print("📝 Загрузка предварительно обученного токенизатора...") - tokenizer = BPETokenizer.load(PATHS["bpe_tokenizer"]) - print(f"✅ Токенизатор загружен (vocab_size={tokenizer.get_vocab_size()})") - else: - tokenizer = train_bpe_tokenizer(TRAIN_TEXTS, BPE_CONFIG) - - # Тестируем токенизатор - test_tokenizer(tokenizer, TEST_PROMPTS[:3]) - - # === Инициализация модели === - model_config = BASE_GPT_CONFIG.copy() - model_config["vocab_size"] = tokenizer.get_vocab_size() - - print(f"\n🔧 Инициализация Llama модели...") - print(f" Размер словаря: {model_config['vocab_size']}") - print(f" Размер эмбеддингов: {model_config['embed_dim']}") - print(f" Количество слоев: {model_config['num_layers']}") - print(f" Количество голов внимания: {model_config['num_heads']}") - - model = Llama(model_config) - - # === Подготовка датасета === - print(f"\n📊 Подготовка датасета...") - train_dataset = TextDataset( - train_texts, tokenizer, block_size=model_config["max_position_embeddings"] - ) - print(f" Размер train датасета: {len(train_dataset)} примеров") - - # === Обучение модели === - print(f"\n🎯 Начало обучения Llama модели...") - - trainer = Trainer( - model=model, - train_dataset=train_dataset, - lr=TRAINING_CONFIG["learning_rate"], - batch_size=TRAINING_CONFIG["batch_size"], - num_epochs=TRAINING_CONFIG["num_epochs"], - warmup_steps=TRAINING_CONFIG["warmup_steps"], - ) - - # Запускаем обучение - trainer.train() - - # === Сохранение модели === - print(f"\n💾 Сохранение модели...") - os.makedirs(os.path.dirname(PATHS["gpt_bpe_model"]), exist_ok=True) - - # Сохраняем модель - torch.save(model.state_dict(), PATHS["gpt_bpe_model"]) - - # Сохраняем конфигурацию - import json - - with open(PATHS["gpt_bpe_config"], "w", encoding="utf-8") as f: - json.dump(model_config, f, indent=2, ensure_ascii=False) - - print(f"✅ Модель сохранена:") - print(f" - {PATHS['gpt_bpe_model']}: веса модели") - print(f" - {PATHS['gpt_bpe_config']}: конфигурация модели") - print(f" - {PATHS['bpe_tokenizer']}: токенизатор") - - # === Тестирование генерации === - print(f"\n🧪 Тестирование генерации текста...") - model.eval() - - for prompt in TEST_PROMPTS[:3]: - print(f"\n🔤 Промпт: '{prompt}'") - - try: - # Кодируем промпт - input_ids = tokenizer.encode(prompt, add_special_tokens=False) - input_tensor = torch.tensor([input_ids], dtype=torch.long) - - # Генерируем текст - with torch.no_grad(): - generated_ids = model.generate( - x=input_tensor, - max_new_tokens=20, - do_sample=True, - temperature=0.8, - ) - - # Декодируем результат - generated_text = tokenizer.decode(generated_ids[0].tolist()) - generated_part = generated_text[len(prompt) :] - - print(f"🎯 Сгенерировано: '{generated_part}'") - print(f"📄 Полный текст: '{generated_text}'") - - except Exception as e: - print(f"❌ Ошибка генерации: {e}") - - # === Сохранение результатов === - results = { - "experiment": experiment_name, - "model_config": model_config, - "training_config": TRAINING_CONFIG, - "tokenizer_vocab_size": tokenizer.get_vocab_size(), - "final_loss": "см. логи обучения", # В реальном эксперименте можно сохранить final loss - } - - logger.save_logs("checkpoints/llm_only_training_logs.json") - - print(f"\n🎉 Эксперимент завершен успешно!") - print(f"\n💡 Для использования обученной модели:") - print(f" uv run python experiments/llm_only/generate_gpt_bpe.py") - - except Exception as e: - print(f"❌ Ошибка в эксперименте: {e}") - import traceback - - traceback.print_exc() - - -if __name__ == "__main__": - main()