mirror of
https://github.com/pese-git/llm-arch-research.git
synced 2026-01-24 05:21:16 +00:00
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
This commit is contained in:
72
README.md
72
README.md
@@ -91,16 +91,76 @@ uv sync
|
|||||||
uv sync --extra dev
|
uv sync --extra dev
|
||||||
```
|
```
|
||||||
|
|
||||||
### Запуск обучения GPT
|
## ⚡ Работа с экспериментами (experiments/llm_only)
|
||||||
|
|
||||||
|
В папке `experiments/llm_only` вы найдете универсальный скрипт для обучения и генерации LLM без HuggingFace.
|
||||||
|
Архитектура позволяет управлять выбором модели, типом действия и параметрами через аргументы командной строки и отдельные JSON-конфиги.
|
||||||
|
|
||||||
|
### Основные файлы и директории
|
||||||
|
|
||||||
|
- `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
|
```bash
|
||||||
# Обучение базовой GPT модели
|
python experiments/llm_only/run_llm_experiment.py --model <название_модели> --action <train/generate> --config experiments/llm_only/configs/<имя_конфига>.json
|
||||||
uv run python experiments/llm_only/train_gpt_bpe.py
|
|
||||||
|
|
||||||
# Обучение с интеграцией HuggingFace
|
|
||||||
uv run python experiments/hf_integration/simple_hf_training.py
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Примеры:
|
||||||
|
|
||||||
|
- Обучить 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
|
### Тестирование hf-proxy
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
19
experiments/llm_only/configs/gpt2_generate.json
Normal file
19
experiments/llm_only/configs/gpt2_generate.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
|
||||||
23
experiments/llm_only/configs/gpt2_train.json
Normal file
23
experiments/llm_only/configs/gpt2_train.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"bpe_tokenizer": "checkpoints/bpe_tokenizer.json",
|
||||||
|
"bpe_vocab_size": 1000,
|
||||||
|
"bpe_special_tokens": ["<pad>", "<unk>", "<bos>", "<eos>"],
|
||||||
|
"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"
|
||||||
|
}
|
||||||
19
experiments/llm_only/configs/gpt_generate.json
Normal file
19
experiments/llm_only/configs/gpt_generate.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
|
||||||
23
experiments/llm_only/configs/gpt_train.json
Normal file
23
experiments/llm_only/configs/gpt_train.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"bpe_tokenizer": "checkpoints/bpe_tokenizer.json",
|
||||||
|
"bpe_vocab_size": 1000,
|
||||||
|
"bpe_special_tokens": ["<pad>", "<unk>", "<bos>", "<eos>"],
|
||||||
|
"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"
|
||||||
|
}
|
||||||
19
experiments/llm_only/configs/llama_generate.json
Normal file
19
experiments/llm_only/configs/llama_generate.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
|
||||||
23
experiments/llm_only/configs/llama_train.json
Normal file
23
experiments/llm_only/configs/llama_train.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"bpe_tokenizer": "checkpoints/bpe_tokenizer.json",
|
||||||
|
"bpe_vocab_size": 1000,
|
||||||
|
"bpe_special_tokens": ["<pad>", "<unk>", "<bos>", "<eos>"],
|
||||||
|
"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"
|
||||||
|
}
|
||||||
@@ -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()
|
|
||||||
@@ -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()
|
|
||||||
167
experiments/llm_only/run_llm_experiment.py
Normal file
167
experiments/llm_only/run_llm_experiment.py
Normal file
@@ -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()
|
||||||
@@ -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()
|
|
||||||
@@ -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()
|
|
||||||
@@ -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()
|
|
||||||
Reference in New Issue
Block a user