mirror of
https://github.com/pese-git/llm-arch-research.git
synced 2026-01-23 21:10:54 +00:00
feat: initial project setup with LLM architecture and HF integration
- Add LLM library with GPT model implementation - Add hf-proxy for HuggingFace integration - Add experiments for training and generation - Add comprehensive documentation and examples - Configure uv workspace with proper dependencies
This commit is contained in:
131
experiments/README.md
Normal file
131
experiments/README.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# Эксперименты с LLM архитектурами
|
||||
|
||||
Унифицированная структура экспериментов для обучения и генерации текста моделями LLM.
|
||||
|
||||
## 📁 Структура экспериментов
|
||||
|
||||
```
|
||||
experiments/
|
||||
├── llm_only/ # Эксперименты только с библиотекой llm
|
||||
│ ├── train_gpt_bpe.py # Обучение GPT с BPE токенизатором
|
||||
│ └── generate_gpt_bpe.py # Генерация с GPT + BPE
|
||||
├── hf_integration/ # Эксперименты с hf-proxy
|
||||
│ ├── train_with_hf_trainer.py # Обучение через HF Trainer
|
||||
│ └── generate_with_hf_tools.py # Генерация через HF инструменты
|
||||
├── shared/ # Общие утилиты
|
||||
│ ├── data.py # Загрузка и подготовка данных
|
||||
│ └── configs.py # Конфигурации моделей
|
||||
└── README.md # Этот файл
|
||||
```
|
||||
|
||||
## 🚀 Быстрый старт
|
||||
|
||||
### 1. Только библиотека llm (автономный режим)
|
||||
|
||||
```bash
|
||||
# Обучение GPT модели с собственным BPE токенизатором
|
||||
uv run python experiments/llm_only/train_gpt_bpe.py
|
||||
|
||||
# Генерация текста обученной моделью
|
||||
uv run python experiments/llm_only/generate_gpt_bpe.py
|
||||
```
|
||||
|
||||
### 2. Интеграция с HuggingFace через hf-proxy
|
||||
|
||||
```bash
|
||||
# Обучение через HuggingFace Trainer
|
||||
uv run python experiments/hf_integration/train_with_hf_trainer.py
|
||||
|
||||
# Генерация через HF инструменты
|
||||
uv run python experiments/hf_integration/generate_with_hf_tools.py
|
||||
```
|
||||
|
||||
## 📊 Сравнение подходов
|
||||
|
||||
| Аспект | Только llm | С hf-proxy |
|
||||
|--------|------------|------------|
|
||||
| **Зависимости** | Только PyTorch | + HuggingFace Transformers |
|
||||
| **Обучение** | Собственный Trainer | HF Trainer |
|
||||
| **Генерация** | Прямой вызов модели | HF pipeline & интерфейсы |
|
||||
| **Гибкость** | Полный контроль | Совместимость с HF экосистемой |
|
||||
| **Сложность** | Проще | Более сложная настройка |
|
||||
|
||||
## 🔧 Конфигурация
|
||||
|
||||
Все эксперименты используют общие конфигурации из `shared/configs.py`:
|
||||
|
||||
- **Модели**: базовые, маленькие и большие конфигурации GPT
|
||||
- **Токенизаторы**: параметры BPE обучения
|
||||
- **Обучение**: гиперпараметры обучения
|
||||
- **Генерация**: параметры генерации текста
|
||||
|
||||
## 📈 Результаты
|
||||
|
||||
Эксперименты сохраняют:
|
||||
- Обученные модели в `checkpoints/`
|
||||
- Токенизаторы в формате JSON
|
||||
- Логи обучения и генерации
|
||||
- Конфигурации моделей
|
||||
|
||||
## 🎯 Примеры использования
|
||||
|
||||
### Автономное использование (только llm)
|
||||
|
||||
```python
|
||||
from llm.models.gpt import GPT
|
||||
from llm.tokenizers import BPETokenizer
|
||||
|
||||
# Загрузка обученной модели
|
||||
model = GPT(config)
|
||||
model.load_state_dict(torch.load("checkpoints/gpt-bpe/model.pt"))
|
||||
|
||||
# Загрузка токенизатора
|
||||
tokenizer = BPETokenizer.load("checkpoints/bpe_tokenizer.json")
|
||||
|
||||
# Генерация текста
|
||||
input_ids = tokenizer.encode("промпт")
|
||||
generated = model.generate(input_ids)
|
||||
```
|
||||
|
||||
### Интеграция с HF (через hf-proxy)
|
||||
|
||||
```python
|
||||
from hf_proxy import HFAdapter, HFTokenizerAdapter
|
||||
|
||||
# Загрузка через адаптеры
|
||||
hf_model = HFAdapter.from_pretrained("checkpoints/hf-trained/pytorch_model.bin")
|
||||
hf_tokenizer = HFTokenizerAdapter.from_pretrained("checkpoints/hf-bpe-tokenizer")
|
||||
|
||||
# Использование с HF инструментами
|
||||
from transformers import pipeline
|
||||
pipe = pipeline("text-generation", model=hf_model, tokenizer=hf_tokenizer)
|
||||
```
|
||||
|
||||
## 🔍 Мониторинг
|
||||
|
||||
- **Логи обучения**: автоматически сохраняются в JSON
|
||||
- **Метрики**: loss, длина генерации, эффективность токенизации
|
||||
- **Визуализация**: можно интегрировать с TensorBoard через HF Trainer
|
||||
|
||||
## 🛠️ Разработка
|
||||
|
||||
### Добавление нового эксперимента
|
||||
|
||||
1. Создайте файл в соответствующей директории (`llm_only/` или `hf_integration/`)
|
||||
2. Используйте общие утилиты из `shared/`
|
||||
3. Сохраняйте результаты в стандартизированные пути
|
||||
4. Документируйте конфигурации и результаты
|
||||
|
||||
### Модификация конфигураций
|
||||
|
||||
Измените соответствующие секции в `shared/configs.py`:
|
||||
- `BASE_GPT_CONFIG` - параметры модели
|
||||
- `BPE_CONFIG` - параметры токенизатора
|
||||
- `TRAINING_CONFIG` - параметры обучения
|
||||
- `GENERATION_CONFIG` - параметры генерации
|
||||
|
||||
## 📚 Дополнительные ресурсы
|
||||
|
||||
- [Документация llm библиотеки](../llm/README.md)
|
||||
- [Документация hf-proxy](../hf-proxy/README.md)
|
||||
- [Примеры использования](../notebooks/)
|
||||
372
experiments/hf_integration/generate_with_hf_tools.py
Normal file
372
experiments/hf_integration/generate_with_hf_tools.py
Normal file
@@ -0,0 +1,372 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Experiment: generate_with_hf_tools.py
|
||||
Description: Генерация текста обученной GPT моделью через HuggingFace инструменты.
|
||||
Использует hf-proxy для интеграции кастомной модели с HF экосистемой.
|
||||
"""
|
||||
|
||||
import torch
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Добавляем путь к shared модулям
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from hf_proxy import HFAdapter, HFTokenizerAdapter, create_hf_pipeline
|
||||
|
||||
from shared.configs import (
|
||||
TEST_PROMPTS, GENERATION_CONFIG, PATHS
|
||||
)
|
||||
from shared.data import (
|
||||
print_experiment_info, ensure_directories, ExperimentLogger
|
||||
)
|
||||
|
||||
|
||||
def load_hf_model_and_tokenizer() -> tuple:
|
||||
"""
|
||||
Загружает модель и токенизатор в формате HuggingFace.
|
||||
|
||||
Returns:
|
||||
tuple: (hf_model, hf_tokenizer, model_config)
|
||||
"""
|
||||
# Используем упрощенную версию модели
|
||||
model_path = "checkpoints/hf_simple_trained"
|
||||
tokenizer_path = "checkpoints/hf_simple_tokenizer"
|
||||
|
||||
# Проверяем существование файлов
|
||||
if not os.path.exists(model_path):
|
||||
raise FileNotFoundError(
|
||||
f"Модель не найдена: {model_path}\n"
|
||||
f"Сначала обучите модель: uv run python experiments/hf_integration/simple_hf_training.py"
|
||||
)
|
||||
|
||||
if not os.path.exists(tokenizer_path):
|
||||
raise FileNotFoundError(
|
||||
f"Токенизатор не найден: {tokenizer_path}"
|
||||
)
|
||||
|
||||
# Загружаем адаптированный токенизатор
|
||||
print("🔧 Загрузка адаптированного токенизатора...")
|
||||
hf_tokenizer = HFTokenizerAdapter.from_pretrained(tokenizer_path)
|
||||
print(f"✅ Токенизатор загружен (vocab_size={hf_tokenizer.vocab_size})")
|
||||
|
||||
# Загружаем конфигурацию модели
|
||||
import json
|
||||
config_path = os.path.join(model_path, "config.json")
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
model_config = json.load(f)
|
||||
|
||||
# Загружаем модель через HFAdapter с правильной конфигурацией
|
||||
print("🔧 Загрузка адаптированной модели...")
|
||||
model_bin_path = os.path.join(model_path, "pytorch_model.bin")
|
||||
|
||||
# Создаем конфигурацию из сохраненного config.json
|
||||
from hf_proxy import HFAdapterConfig
|
||||
hf_config = HFAdapterConfig(
|
||||
vocab_size=model_config["vocab_size"],
|
||||
hidden_size=model_config["hidden_size"],
|
||||
num_hidden_layers=model_config["num_hidden_layers"],
|
||||
num_attention_heads=model_config["num_attention_heads"],
|
||||
max_position_embeddings=model_config["max_position_embeddings"],
|
||||
hidden_dropout_prob=model_config.get("hidden_dropout_prob", 0.1),
|
||||
attention_probs_dropout_prob=model_config.get("attention_probs_dropout_prob", 0.1),
|
||||
)
|
||||
|
||||
hf_model = HFAdapter.from_pretrained(model_bin_path, hf_config=hf_config)
|
||||
hf_model.eval()
|
||||
print("✅ Модель загружена")
|
||||
|
||||
return hf_model, hf_tokenizer, model_config
|
||||
|
||||
|
||||
def test_hf_pipeline(hf_model, hf_tokenizer):
|
||||
"""
|
||||
Тестирует создание HuggingFace pipeline.
|
||||
|
||||
Args:
|
||||
hf_model: Адаптированная модель
|
||||
hf_tokenizer: Адаптированный токенизатор
|
||||
"""
|
||||
print("\n🧪 Тестирование HuggingFace pipeline...")
|
||||
|
||||
try:
|
||||
# Создаем pipeline
|
||||
pipe = create_hf_pipeline(
|
||||
hf_model,
|
||||
tokenizer=hf_tokenizer,
|
||||
device="cpu",
|
||||
max_length=50,
|
||||
do_sample=True,
|
||||
temperature=0.7
|
||||
)
|
||||
|
||||
print("✅ HuggingFace pipeline создан")
|
||||
|
||||
# Тестируем pipeline
|
||||
test_prompts = TEST_PROMPTS[:3]
|
||||
|
||||
for prompt in test_prompts:
|
||||
print(f"\n🔤 Промпт: '{prompt}'")
|
||||
|
||||
try:
|
||||
result = pipe(prompt, max_new_tokens=20)
|
||||
print(f"🎯 Результат: {result[0]['generated_text']}")
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка в pipeline: {e}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка создания pipeline: {e}")
|
||||
|
||||
|
||||
def generate_with_hf_model(hf_model, hf_tokenizer, prompt: str, config: dict) -> str:
|
||||
"""
|
||||
Генерирует текст через адаптированную модель HF.
|
||||
|
||||
Args:
|
||||
hf_model: Адаптированная модель
|
||||
hf_tokenizer: Адаптированный токенизатор
|
||||
prompt: Входной текст
|
||||
config: Конфигурация генерации
|
||||
|
||||
Returns:
|
||||
str: Сгенерированный текст
|
||||
"""
|
||||
print(f"🔤 Промпт: '{prompt}'")
|
||||
print(f"📊 Параметры: max_tokens={config['max_new_tokens']}, "
|
||||
f"temp={config['temperature']}, sample={config['do_sample']}")
|
||||
|
||||
# Кодируем через адаптированный токенизатор
|
||||
inputs = hf_tokenizer(prompt, return_tensors="pt")
|
||||
|
||||
print(f"🎯 Токены промпта: {inputs['input_ids'].tolist()[0]}")
|
||||
print("🔄 Генерация через HF адаптер...")
|
||||
|
||||
# Генерируем через адаптированную модель
|
||||
with torch.no_grad():
|
||||
generated_ids = hf_model.generate(
|
||||
input_ids=inputs['input_ids'],
|
||||
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 = hf_tokenizer.decode(generated_ids[0], skip_special_tokens=True)
|
||||
|
||||
return generated_text
|
||||
|
||||
|
||||
def test_different_hf_strategies(hf_model, hf_tokenizer, prompt: str):
|
||||
"""
|
||||
Тестирует разные стратегии генерации через HF интерфейс.
|
||||
|
||||
Args:
|
||||
hf_model: Адаптированная модель
|
||||
hf_tokenizer: Адаптированный токенизатор
|
||||
prompt: Тестовый промпт
|
||||
"""
|
||||
print(f"\n🎭 Сравнение стратегий генерации через HF для промпта: '{prompt}'")
|
||||
print("=" * 70)
|
||||
|
||||
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_with_hf_model(hf_model, hf_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_hf_tokenization(hf_tokenizer, texts: list):
|
||||
"""
|
||||
Анализирует токенизацию через адаптированный токенизатор.
|
||||
|
||||
Args:
|
||||
hf_tokenizer: Адаптированный токенизатор
|
||||
texts: Список текстов для анализа
|
||||
"""
|
||||
print(f"\n🔍 Анализ токенизации через HF адаптер:")
|
||||
print("=" * 60)
|
||||
|
||||
for i, text in enumerate(texts):
|
||||
print(f"\nТекст {i+1}: '{text}'")
|
||||
|
||||
# Токенизация через адаптер
|
||||
inputs = hf_tokenizer(text, return_tensors="pt")
|
||||
tokens = inputs['input_ids'].tolist()[0]
|
||||
token_strings = hf_tokenizer.tokenize(text)
|
||||
|
||||
print(f" Токены (ID): {tokens}")
|
||||
print(f" Токены (текст): {token_strings}")
|
||||
print(f" Количество токенов: {len(tokens)}")
|
||||
|
||||
# Декодирование обратно
|
||||
decoded = hf_tokenizer.decode(tokens)
|
||||
print(f" Декодированный: '{decoded}'")
|
||||
|
||||
if text == decoded:
|
||||
print(f" ✅ Декодирование корректно")
|
||||
else:
|
||||
print(f" ⚠️ Расхождения")
|
||||
|
||||
|
||||
def interactive_hf_generation(hf_model, hf_tokenizer):
|
||||
"""
|
||||
Режим интерактивной генерации через HF интерфейс.
|
||||
|
||||
Args:
|
||||
hf_model: Адаптированная модель
|
||||
hf_tokenizer: Адаптированный токенизатор
|
||||
"""
|
||||
print(f"\n💬 Интерактивная генерация через HF (для выхода введите 'exit')")
|
||||
print("-" * 60)
|
||||
|
||||
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_with_hf_model(hf_model, hf_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 = "Генерация текста через HF инструменты (с hf-proxy)"
|
||||
experiment_config = {
|
||||
"model": "GPT через HFAdapter",
|
||||
"tokenizer": "BPE через HFTokenizerAdapter",
|
||||
"инструменты": "HuggingFace pipeline & генерация",
|
||||
"стратегия": "интеграция с HF экосистемой"
|
||||
}
|
||||
|
||||
print_experiment_info(experiment_name, experiment_config)
|
||||
ensure_directories()
|
||||
logger = ExperimentLogger(experiment_name)
|
||||
|
||||
try:
|
||||
# Загружаем модель и токенизатор в HF формате
|
||||
hf_model, hf_tokenizer, model_config = load_hf_model_and_tokenizer()
|
||||
|
||||
# === Анализ токенизации ===
|
||||
analysis_texts = [
|
||||
"Искусственный интеллект",
|
||||
"Нейронные сети",
|
||||
"Машинное обучение"
|
||||
]
|
||||
analyze_hf_tokenization(hf_tokenizer, analysis_texts)
|
||||
|
||||
# === Тестирование HF pipeline ===
|
||||
test_hf_pipeline(hf_model, hf_tokenizer)
|
||||
|
||||
# === Генерация с разными промптами ===
|
||||
print(f"\n🎯 Генерация текста через HF адаптер")
|
||||
print("=" * 60)
|
||||
|
||||
for i, prompt in enumerate(TEST_PROMPTS):
|
||||
print(f"\n📝 Пример {i+1}/{len(TEST_PROMPTS)}")
|
||||
print("-" * 40)
|
||||
|
||||
try:
|
||||
generated = generate_with_hf_model(hf_model, hf_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"hf_generation_length_{i}", len(generated))
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка при генерации: {e}")
|
||||
continue
|
||||
|
||||
# === Сравнение стратегий генерации ===
|
||||
test_prompt = "Искусственный"
|
||||
test_different_hf_strategies(hf_model, hf_tokenizer, test_prompt)
|
||||
|
||||
# === Интерактивная генерация ===
|
||||
interactive_hf_generation(hf_model, hf_tokenizer)
|
||||
|
||||
# === Сохранение результатов ===
|
||||
logger.save_logs("checkpoints/hf_integration_generation_logs.json")
|
||||
|
||||
print(f"\n🎉 Эксперимент с HF интеграцией завершен успешно!")
|
||||
print(f"\n📚 Достигнутая интеграция:")
|
||||
print(f" ✅ Загрузка модели и токенизатора в HF формате")
|
||||
print(f" ✅ Использование HF pipeline")
|
||||
print(f" ✅ Генерация через стандартные HF интерфейсы")
|
||||
print(f" ✅ Совместимость с HF экосистемой")
|
||||
|
||||
except FileNotFoundError as e:
|
||||
print(f"❌ {e}")
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка в эксперименте: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
285
experiments/hf_integration/simple_hf_training.py
Normal file
285
experiments/hf_integration/simple_hf_training.py
Normal file
@@ -0,0 +1,285 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Experiment: simple_hf_training.py
|
||||
Description: Упрощенное обучение GPT модели с использованием hf-proxy.
|
||||
Использует ручное обучение вместо сложного HuggingFace Trainer.
|
||||
"""
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
|
||||
# Добавляем путь к 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 hf_proxy import HFAdapter, HFTokenizerAdapter
|
||||
|
||||
from shared.configs import (
|
||||
TRAIN_TEXTS, BASE_GPT_CONFIG, BPE_CONFIG,
|
||||
TRAINING_CONFIG, PATHS, TEST_PROMPTS
|
||||
)
|
||||
|
||||
|
||||
def create_dataset(hf_tokenizer, texts, max_length=128):
|
||||
"""
|
||||
Создает простой датасет для обучения.
|
||||
|
||||
Args:
|
||||
hf_tokenizer: Адаптированный токенизатор
|
||||
texts: Список текстов
|
||||
max_length: Максимальная длина последовательности
|
||||
|
||||
Returns:
|
||||
list: Список тензоров input_ids
|
||||
"""
|
||||
dataset = []
|
||||
|
||||
for text in texts:
|
||||
# Токенизируем текст
|
||||
inputs = hf_tokenizer(
|
||||
text,
|
||||
max_length=max_length,
|
||||
truncation=True,
|
||||
padding=False,
|
||||
return_tensors="pt"
|
||||
)
|
||||
|
||||
input_ids = inputs['input_ids'][0]
|
||||
|
||||
# Создаем метки для языкового моделирования
|
||||
labels = input_ids.clone()
|
||||
|
||||
dataset.append({
|
||||
'input_ids': input_ids,
|
||||
'labels': labels
|
||||
})
|
||||
|
||||
return dataset
|
||||
|
||||
|
||||
def manual_training_loop(hf_model, hf_tokenizer, train_texts, val_texts, config):
|
||||
"""
|
||||
Ручной цикл обучения без использования Trainer.
|
||||
|
||||
Args:
|
||||
hf_model: Адаптированная модель
|
||||
hf_tokenizer: Адаптированный токенизатор
|
||||
train_texts: Тексты для обучения
|
||||
val_texts: Тексты для валидации
|
||||
config: Конфигурация обучения
|
||||
|
||||
Returns:
|
||||
dict: Результаты обучения
|
||||
"""
|
||||
print("🎯 Запуск ручного обучения...")
|
||||
|
||||
# Создаем датасеты
|
||||
train_dataset = create_dataset(hf_tokenizer, train_texts)
|
||||
val_dataset = create_dataset(hf_tokenizer, val_texts)
|
||||
|
||||
print(f"📊 Данные: {len(train_dataset)} train, {len(val_dataset)} validation")
|
||||
|
||||
# Оптимизатор
|
||||
optimizer = torch.optim.AdamW(
|
||||
hf_model.parameters(),
|
||||
lr=config["learning_rate"]
|
||||
)
|
||||
|
||||
# Функция потерь
|
||||
loss_fn = nn.CrossEntropyLoss()
|
||||
|
||||
# Обучение
|
||||
hf_model.train()
|
||||
train_losses = []
|
||||
val_losses = []
|
||||
|
||||
for epoch in range(config["num_epochs"]):
|
||||
print(f"\n📅 Эпоха {epoch + 1}/{config['num_epochs']}")
|
||||
|
||||
# Обучение
|
||||
epoch_train_loss = 0
|
||||
for i, batch in enumerate(train_dataset):
|
||||
optimizer.zero_grad()
|
||||
|
||||
input_ids = batch['input_ids'].unsqueeze(0) # [1, seq_len]
|
||||
labels = batch['labels'].unsqueeze(0) # [1, seq_len]
|
||||
|
||||
# Forward pass
|
||||
outputs = hf_model(input_ids=input_ids, labels=labels)
|
||||
loss = outputs.loss
|
||||
|
||||
# Backward pass
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
|
||||
epoch_train_loss += loss.item()
|
||||
|
||||
if i % 5 == 0:
|
||||
print(f" Batch {i}/{len(train_dataset)}: loss = {loss.item():.4f}")
|
||||
|
||||
avg_train_loss = epoch_train_loss / len(train_dataset)
|
||||
train_losses.append(avg_train_loss)
|
||||
print(f" 📊 Средняя train loss: {avg_train_loss:.4f}")
|
||||
|
||||
# Валидация
|
||||
hf_model.eval()
|
||||
epoch_val_loss = 0
|
||||
with torch.no_grad():
|
||||
for batch in val_dataset:
|
||||
input_ids = batch['input_ids'].unsqueeze(0)
|
||||
labels = batch['labels'].unsqueeze(0)
|
||||
|
||||
outputs = hf_model(input_ids=input_ids, labels=labels)
|
||||
epoch_val_loss += outputs.loss.item()
|
||||
|
||||
avg_val_loss = epoch_val_loss / len(val_dataset)
|
||||
val_losses.append(avg_val_loss)
|
||||
print(f" 📊 Средняя val loss: {avg_val_loss:.4f}")
|
||||
|
||||
hf_model.train()
|
||||
|
||||
return {
|
||||
'train_losses': train_losses,
|
||||
'val_losses': val_losses,
|
||||
'final_train_loss': train_losses[-1],
|
||||
'final_val_loss': val_losses[-1]
|
||||
}
|
||||
|
||||
|
||||
def test_generation_after_training(hf_model, hf_tokenizer, test_prompts):
|
||||
"""
|
||||
Тестирует генерацию после обучения.
|
||||
|
||||
Args:
|
||||
hf_model: Обученная модель
|
||||
hf_tokenizer: Токенизатор
|
||||
test_prompts: Тестовые промпты
|
||||
"""
|
||||
print("\n🧪 Тестирование генерации после обучения...")
|
||||
hf_model.eval()
|
||||
|
||||
for prompt in test_prompts[:3]:
|
||||
print(f"\n🔤 Промпт: '{prompt}'")
|
||||
|
||||
try:
|
||||
inputs = hf_tokenizer(prompt, return_tensors="pt")
|
||||
|
||||
with torch.no_grad():
|
||||
generated = hf_model.generate(
|
||||
input_ids=inputs['input_ids'],
|
||||
max_new_tokens=20,
|
||||
do_sample=True,
|
||||
temperature=0.8
|
||||
)
|
||||
|
||||
generated_text = hf_tokenizer.decode(generated[0], skip_special_tokens=True)
|
||||
print(f"🎯 Результат: '{generated_text}'")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка генерации: {e}")
|
||||
|
||||
|
||||
def main():
|
||||
"""Основная функция эксперимента."""
|
||||
print("=" * 60)
|
||||
print("🚀 УПРОЩЕННОЕ ОБУЧЕНИЕ GPT С HF-PROXY")
|
||||
print("=" * 60)
|
||||
|
||||
try:
|
||||
# === Подготовка данных ===
|
||||
print("🔧 Подготовка данных...")
|
||||
train_texts = TRAIN_TEXTS[:10] # Используем меньше данных для быстрого тестирования
|
||||
val_texts = TRAIN_TEXTS[10:12]
|
||||
|
||||
print(f"📊 Данные: {len(train_texts)} train, {len(val_texts)} validation")
|
||||
|
||||
# === Подготовка токенизатора ===
|
||||
print("🔧 Подготовка токенизатора...")
|
||||
llm_tokenizer = BPETokenizer()
|
||||
llm_tokenizer.train(
|
||||
texts=train_texts,
|
||||
vocab_size=BPE_CONFIG["vocab_size"],
|
||||
special_tokens=BPE_CONFIG["special_tokens"]
|
||||
)
|
||||
|
||||
hf_tokenizer = HFTokenizerAdapter(llm_tokenizer)
|
||||
print(f"✅ Токенизатор создан (vocab_size={hf_tokenizer.vocab_size})")
|
||||
|
||||
# === Подготовка модели ===
|
||||
print("🔧 Подготовка модели...")
|
||||
model_config = BASE_GPT_CONFIG.copy()
|
||||
model_config["vocab_size"] = hf_tokenizer.vocab_size
|
||||
|
||||
llm_model = GPT(model_config)
|
||||
hf_model = HFAdapter.from_llm_model(llm_model)
|
||||
print(f"✅ Модель создана")
|
||||
|
||||
# === Тестирование до обучения ===
|
||||
print("\n🧪 Тестирование до обучения...")
|
||||
test_generation_after_training(hf_model, hf_tokenizer, TEST_PROMPTS)
|
||||
|
||||
# === Обучение ===
|
||||
print(f"\n🎯 Обучение модели...")
|
||||
training_config = {
|
||||
"learning_rate": TRAINING_CONFIG["learning_rate"],
|
||||
"num_epochs": 2, # Меньше эпох для быстрого тестирования
|
||||
"batch_size": TRAINING_CONFIG["batch_size"]
|
||||
}
|
||||
|
||||
results = manual_training_loop(
|
||||
hf_model, hf_tokenizer, train_texts, val_texts, training_config
|
||||
)
|
||||
|
||||
print(f"\n📊 Результаты обучения:")
|
||||
print(f" Final train loss: {results['final_train_loss']:.4f}")
|
||||
print(f" Final val loss: {results['final_val_loss']:.4f}")
|
||||
|
||||
# === Тестирование после обучения ===
|
||||
print("\n🧪 Тестирование после обучения...")
|
||||
test_generation_after_training(hf_model, hf_tokenizer, TEST_PROMPTS)
|
||||
|
||||
# === Сохранение модели ===
|
||||
print(f"\n💾 Сохранение модели...")
|
||||
|
||||
# Создаем директории
|
||||
os.makedirs("checkpoints/hf_simple_trained", exist_ok=True)
|
||||
os.makedirs("checkpoints/hf_simple_tokenizer", exist_ok=True)
|
||||
|
||||
# Сохраняем токенизатор
|
||||
hf_tokenizer.save_pretrained("checkpoints/hf_simple_tokenizer")
|
||||
print("✅ Токенизатор сохранен")
|
||||
|
||||
# Сохраняем модель
|
||||
HFAdapter.save_pretrained(
|
||||
hf_model,
|
||||
"checkpoints/hf_simple_trained",
|
||||
tokenizer=hf_tokenizer
|
||||
)
|
||||
print("✅ Модель сохранена")
|
||||
|
||||
# Сохраняем результаты
|
||||
results_path = "checkpoints/simple_training_results.json"
|
||||
with open(results_path, 'w', encoding='utf-8') as f:
|
||||
json.dump({
|
||||
'training_config': training_config,
|
||||
'model_config': model_config,
|
||||
'results': results
|
||||
}, f, indent=2, ensure_ascii=False)
|
||||
print(f"✅ Результаты сохранены в {results_path}")
|
||||
|
||||
print(f"\n🎉 Упрощенное обучение завершено успешно!")
|
||||
print(f"\n💡 Для использования обученной модели:")
|
||||
print(f" uv run python experiments/hf_integration/generate_with_hf_tools.py")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка в эксперименте: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
206
experiments/hf_integration/test_hf_proxy.py
Normal file
206
experiments/hf_integration/test_hf_proxy.py
Normal file
@@ -0,0 +1,206 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test: test_hf_proxy.py
|
||||
Description: Тестирование базовой функциональности hf-proxy без сложных зависимостей.
|
||||
"""
|
||||
|
||||
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 hf_proxy import HFAdapter, HFTokenizerAdapter
|
||||
|
||||
from shared.configs import (
|
||||
TRAIN_TEXTS, BASE_GPT_CONFIG, BPE_CONFIG,
|
||||
TEST_PROMPTS, GENERATION_CONFIG
|
||||
)
|
||||
|
||||
|
||||
def test_basic_hf_integration():
|
||||
"""Тестирует базовую интеграцию hf-proxy."""
|
||||
print("🧪 Тестирование базовой интеграции hf-proxy...")
|
||||
|
||||
# === Подготовка токенизатора ===
|
||||
print("1. Подготовка токенизатора...")
|
||||
llm_tokenizer = BPETokenizer()
|
||||
llm_tokenizer.train(
|
||||
texts=TRAIN_TEXTS,
|
||||
vocab_size=BPE_CONFIG["vocab_size"],
|
||||
special_tokens=BPE_CONFIG["special_tokens"]
|
||||
)
|
||||
|
||||
hf_tokenizer = HFTokenizerAdapter(llm_tokenizer)
|
||||
print(f" ✅ Токенизатор создан (vocab_size={hf_tokenizer.vocab_size})")
|
||||
|
||||
# === Подготовка модели ===
|
||||
print("2. Подготовка модели...")
|
||||
model_config = BASE_GPT_CONFIG.copy()
|
||||
model_config["vocab_size"] = hf_tokenizer.vocab_size
|
||||
|
||||
llm_model = GPT(model_config)
|
||||
hf_model = HFAdapter.from_llm_model(llm_model)
|
||||
print(f" ✅ Модель создана")
|
||||
|
||||
# === Тестирование токенизации ===
|
||||
print("3. Тестирование токенизации...")
|
||||
test_texts = ["Искусственный интеллект", "Нейронные сети"]
|
||||
|
||||
for text in test_texts:
|
||||
print(f" 📝 Текст: '{text}'")
|
||||
|
||||
# Оригинальный токенизатор
|
||||
original_tokens = llm_tokenizer.encode(text)
|
||||
print(f" Оригинальный: {len(original_tokens)} токенов")
|
||||
|
||||
# HF адаптер
|
||||
hf_inputs = hf_tokenizer(text, return_tensors="pt")
|
||||
print(f" HF адаптер: {hf_inputs['input_ids'].shape}")
|
||||
|
||||
# Декодирование
|
||||
decoded = hf_tokenizer.decode(hf_inputs['input_ids'][0])
|
||||
print(f" Декодированный: '{decoded}'")
|
||||
|
||||
# === Тестирование forward pass ===
|
||||
print("4. Тестирование forward pass...")
|
||||
for text in test_texts:
|
||||
hf_inputs = hf_tokenizer(text, return_tensors="pt")
|
||||
|
||||
with torch.no_grad():
|
||||
outputs = hf_model(**hf_inputs)
|
||||
|
||||
print(f" 📝 '{text}' -> logits: {outputs.logits.shape}")
|
||||
|
||||
# === Тестирование генерации ===
|
||||
print("5. Тестирование генерации...")
|
||||
hf_model.eval()
|
||||
|
||||
for prompt in TEST_PROMPTS[:3]:
|
||||
print(f" 🔤 Промпт: '{prompt}'")
|
||||
|
||||
try:
|
||||
inputs = hf_tokenizer(prompt, return_tensors="pt")
|
||||
|
||||
with torch.no_grad():
|
||||
generated = hf_model.generate(
|
||||
input_ids=inputs['input_ids'],
|
||||
max_new_tokens=10,
|
||||
do_sample=True,
|
||||
temperature=0.8
|
||||
)
|
||||
|
||||
generated_text = hf_tokenizer.decode(generated[0], skip_special_tokens=True)
|
||||
print(f" 🎯 Результат: '{generated_text}'")
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ Ошибка: {e}")
|
||||
|
||||
# === Тестирование сохранения/загрузки ===
|
||||
print("6. Тестирование сохранения/загрузки...")
|
||||
try:
|
||||
# Сохраняем токенизатор
|
||||
hf_tokenizer.save_pretrained("test_save/tokenizer")
|
||||
print(" ✅ Токенизатор сохранен")
|
||||
|
||||
# Сохраняем модель
|
||||
HFAdapter.save_pretrained(hf_model, "test_save/model", tokenizer=hf_tokenizer)
|
||||
print(" ✅ Модель сохранена")
|
||||
|
||||
# Загружаем токенизатор
|
||||
loaded_tokenizer = HFTokenizerAdapter.from_pretrained("test_save/tokenizer")
|
||||
print(f" ✅ Токенизатор загружен (vocab_size={loaded_tokenizer.vocab_size})")
|
||||
|
||||
# Загружаем модель
|
||||
model_path = os.path.join("test_save/model", "pytorch_model.bin")
|
||||
loaded_model = HFAdapter.from_pretrained(model_path)
|
||||
print(" ✅ Модель загружена")
|
||||
|
||||
# Проверяем работоспособность загруженной модели
|
||||
test_input = hf_tokenizer("Тест", return_tensors="pt")
|
||||
with torch.no_grad():
|
||||
loaded_outputs = loaded_model(**test_input)
|
||||
print(f" ✅ Загруженная модель работает (logits: {loaded_outputs.logits.shape})")
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ Ошибка сохранения/загрузки: {e}")
|
||||
|
||||
print("\n🎉 Базовое тестирование hf-proxy завершено!")
|
||||
|
||||
|
||||
def test_hf_tokenizer_methods():
|
||||
"""Тестирует различные методы HF токенизатора."""
|
||||
print("\n🧪 Тестирование методов HF токенизатора...")
|
||||
|
||||
# Создаем токенизатор
|
||||
llm_tokenizer = BPETokenizer()
|
||||
llm_tokenizer.train(
|
||||
texts=TRAIN_TEXTS[:5],
|
||||
vocab_size=500,
|
||||
special_tokens=BPE_CONFIG["special_tokens"]
|
||||
)
|
||||
|
||||
hf_tokenizer = HFTokenizerAdapter(llm_tokenizer)
|
||||
|
||||
test_text = "Искусственный интеллект и машинное обучение"
|
||||
|
||||
# Тестируем разные методы
|
||||
print("1. Метод __call__:")
|
||||
result = hf_tokenizer(test_text, return_tensors="pt")
|
||||
print(f" Результат: {result}")
|
||||
|
||||
print("2. Метод encode:")
|
||||
encoded = hf_tokenizer.encode(test_text)
|
||||
print(f" Закодировано: {encoded}")
|
||||
|
||||
print("3. Метод decode:")
|
||||
decoded = hf_tokenizer.decode(encoded)
|
||||
print(f" Декодировано: '{decoded}'")
|
||||
|
||||
print("4. Метод tokenize:")
|
||||
tokens = hf_tokenizer.tokenize(test_text)
|
||||
print(f" Токены: {tokens}")
|
||||
|
||||
print("5. Метод get_vocab:")
|
||||
vocab = hf_tokenizer.get_vocab()
|
||||
print(f" Размер словаря: {len(vocab)}")
|
||||
|
||||
print("✅ Все методы токенизатора работают!")
|
||||
|
||||
|
||||
def main():
|
||||
"""Основная функция тестирования."""
|
||||
print("=" * 60)
|
||||
print("🧪 ТЕСТИРОВАНИЕ HF-PROXY")
|
||||
print("=" * 60)
|
||||
|
||||
try:
|
||||
# Тестируем базовую интеграцию
|
||||
test_basic_hf_integration()
|
||||
|
||||
# Тестируем методы токенизатора
|
||||
test_hf_tokenizer_methods()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("🎉 ВСЕ ТЕСТЫ ПРОЙДЕНЫ УСПЕШНО!")
|
||||
print("=" * 60)
|
||||
print("\n📚 Проверенные функции:")
|
||||
print(" ✅ Создание HF адаптера для токенизатора")
|
||||
print(" ✅ Создание HF адаптера для модели")
|
||||
print(" ✅ Токенизация и декодирование")
|
||||
print(" ✅ Forward pass через адаптированную модель")
|
||||
print(" ✅ Генерация текста")
|
||||
print(" ✅ Сохранение и загрузка моделей")
|
||||
print(" ✅ Все методы HF токенизатора")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ Ошибка в тестировании: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
298
experiments/hf_integration/train_with_hf_trainer.py
Normal file
298
experiments/hf_integration/train_with_hf_trainer.py
Normal file
@@ -0,0 +1,298 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Experiment: train_with_hf_trainer.py
|
||||
Description: Обучение GPT модели через HuggingFace Trainer с использованием hf-proxy.
|
||||
Интегрирует кастомную модель 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 hf_proxy import HFAdapter, HFTokenizerAdapter
|
||||
|
||||
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 setup_hf_training():
|
||||
"""
|
||||
Настраивает окружение для обучения через HuggingFace Trainer.
|
||||
|
||||
Returns:
|
||||
tuple: (hf_model, hf_tokenizer, llm_tokenizer, model_config)
|
||||
"""
|
||||
print("🔧 Настройка HuggingFace обучения...")
|
||||
|
||||
# === Подготовка данных ===
|
||||
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("📝 Загрузка BPE токенизатора...")
|
||||
llm_tokenizer = BPETokenizer.load(PATHS["bpe_tokenizer"])
|
||||
print(f"✅ Токенизатор загружен (vocab_size={llm_tokenizer.get_vocab_size()})")
|
||||
else:
|
||||
print("📝 Обучение BPE токенизатора...")
|
||||
llm_tokenizer = BPETokenizer()
|
||||
llm_tokenizer.train(
|
||||
texts=TRAIN_TEXTS,
|
||||
vocab_size=BPE_CONFIG["vocab_size"],
|
||||
special_tokens=BPE_CONFIG["special_tokens"]
|
||||
)
|
||||
llm_tokenizer.save(PATHS["bpe_tokenizer"])
|
||||
print(f"✅ Токенизатор обучен и сохранен")
|
||||
|
||||
# === Создание адаптера токенизатора ===
|
||||
print("🔧 Создание адаптера HuggingFace для токенизатора...")
|
||||
hf_tokenizer = HFTokenizerAdapter(llm_tokenizer)
|
||||
print(f"✅ Адаптер токенизатора создан")
|
||||
|
||||
# === Инициализация модели ===
|
||||
model_config = BASE_GPT_CONFIG.copy()
|
||||
model_config["vocab_size"] = llm_tokenizer.get_vocab_size()
|
||||
|
||||
print("🔧 Создание GPT модели...")
|
||||
llm_model = GPT(model_config)
|
||||
|
||||
# === Создание адаптера модели ===
|
||||
print("🔧 Создание адаптера HuggingFace для модели...")
|
||||
hf_model = HFAdapter.from_llm_model(llm_model)
|
||||
print(f"✅ Адаптер модели создан")
|
||||
|
||||
return hf_model, hf_tokenizer, llm_tokenizer, model_config, train_texts, val_texts
|
||||
|
||||
|
||||
def test_hf_integration(hf_model, hf_tokenizer, llm_tokenizer):
|
||||
"""
|
||||
Тестирует интеграцию с HuggingFace инструментами.
|
||||
|
||||
Args:
|
||||
hf_model: Адаптированная модель
|
||||
hf_tokenizer: Адаптированный токенизатор
|
||||
llm_tokenizer: Оригинальный токенизатор
|
||||
"""
|
||||
print("\n🧪 Тестирование интеграции с HuggingFace...")
|
||||
|
||||
test_texts = ["Искусственный интеллект", "Нейронные сети"]
|
||||
|
||||
for text in test_texts:
|
||||
print(f"\n🔤 Текст: '{text}'")
|
||||
|
||||
# Тестируем адаптированный токенизатор
|
||||
hf_inputs = hf_tokenizer(text, return_tensors="pt")
|
||||
print(f" HF токенизатор: {hf_inputs['input_ids'].shape}")
|
||||
|
||||
# Тестируем оригинальный токенизатор для сравнения
|
||||
original_tokens = llm_tokenizer.encode(text)
|
||||
print(f" Оригинальный токенизатор: {len(original_tokens)} токенов")
|
||||
|
||||
# Тестируем forward pass через адаптированную модель
|
||||
try:
|
||||
with torch.no_grad():
|
||||
outputs = hf_model(**hf_inputs)
|
||||
print(f" HF forward pass: успешно (logits: {outputs.logits.shape})")
|
||||
except Exception as e:
|
||||
print(f" ❌ HF forward pass: {e}")
|
||||
|
||||
|
||||
def main():
|
||||
"""Основная функция эксперимента."""
|
||||
# === Настройка эксперимента ===
|
||||
experiment_name = "Обучение GPT через HF Trainer (с hf-proxy)"
|
||||
experiment_config = {
|
||||
"model": "GPT через HFAdapter",
|
||||
"tokenizer": "BPE через HFTokenizerAdapter",
|
||||
"trainer": "HuggingFace Trainer",
|
||||
"vocab_size": BPE_CONFIG["vocab_size"],
|
||||
"training_epochs": TRAINING_CONFIG["num_epochs"]
|
||||
}
|
||||
|
||||
print_experiment_info(experiment_name, experiment_config)
|
||||
ensure_directories()
|
||||
logger = ExperimentLogger(experiment_name)
|
||||
|
||||
try:
|
||||
# Настраиваем обучение
|
||||
hf_model, hf_tokenizer, llm_tokenizer, model_config, train_texts, val_texts = setup_hf_training()
|
||||
|
||||
# Тестируем интеграцию
|
||||
test_hf_integration(hf_model, hf_tokenizer, llm_tokenizer)
|
||||
|
||||
# === Подготовка датасетов HuggingFace ===
|
||||
print(f"\n📊 Подготовка датасетов HuggingFace...")
|
||||
|
||||
from datasets import Dataset
|
||||
|
||||
def tokenize_function(examples):
|
||||
"""Функция токенизации для HF datasets."""
|
||||
# Используем адаптированный токенизатор
|
||||
tokenized = hf_tokenizer(
|
||||
examples["text"],
|
||||
truncation=True,
|
||||
padding=False,
|
||||
max_length=model_config["max_position_embeddings"],
|
||||
)
|
||||
tokenized["labels"] = tokenized["input_ids"].copy()
|
||||
return tokenized
|
||||
|
||||
# Создаем датасеты
|
||||
train_dataset = Dataset.from_dict({"text": train_texts})
|
||||
val_dataset = Dataset.from_dict({"text": val_texts})
|
||||
|
||||
# Токенизируем
|
||||
train_dataset = train_dataset.map(
|
||||
tokenize_function,
|
||||
batched=True,
|
||||
remove_columns=train_dataset.column_names,
|
||||
)
|
||||
val_dataset = val_dataset.map(
|
||||
tokenize_function,
|
||||
batched=True,
|
||||
remove_columns=val_dataset.column_names,
|
||||
)
|
||||
|
||||
print(f" Train датасет: {len(train_dataset)} примеров")
|
||||
print(f" Validation датасет: {len(val_dataset)} примеров")
|
||||
|
||||
# === Настройка HuggingFace Trainer ===
|
||||
print(f"\n🔧 Настройка HuggingFace Trainer...")
|
||||
|
||||
from transformers import (
|
||||
Trainer,
|
||||
TrainingArguments,
|
||||
DataCollatorForLanguageModeling
|
||||
)
|
||||
|
||||
# Data collator для языкового моделирования
|
||||
data_collator = DataCollatorForLanguageModeling(
|
||||
tokenizer=hf_tokenizer,
|
||||
mlm=False,
|
||||
pad_to_multiple_of=8,
|
||||
)
|
||||
|
||||
# Аргументы обучения
|
||||
training_args = TrainingArguments(
|
||||
output_dir=PATHS["hf_model"],
|
||||
overwrite_output_dir=True,
|
||||
num_train_epochs=TRAINING_CONFIG["num_epochs"],
|
||||
per_device_train_batch_size=TRAINING_CONFIG["batch_size"],
|
||||
per_device_eval_batch_size=TRAINING_CONFIG["batch_size"],
|
||||
learning_rate=TRAINING_CONFIG["learning_rate"],
|
||||
warmup_steps=TRAINING_CONFIG["warmup_steps"],
|
||||
logging_dir="./logs",
|
||||
logging_steps=10,
|
||||
eval_steps=50,
|
||||
save_steps=100,
|
||||
eval_strategy="steps",
|
||||
save_strategy="steps",
|
||||
load_best_model_at_end=True,
|
||||
metric_for_best_model="loss",
|
||||
greater_is_better=False,
|
||||
dataloader_pin_memory=False,
|
||||
report_to=None,
|
||||
)
|
||||
|
||||
# Создаем Trainer
|
||||
trainer = Trainer(
|
||||
model=hf_model,
|
||||
args=training_args,
|
||||
train_dataset=train_dataset,
|
||||
eval_dataset=val_dataset,
|
||||
data_collator=data_collator,
|
||||
)
|
||||
|
||||
print("✅ HuggingFace Trainer настроен")
|
||||
|
||||
# === Запуск обучения ===
|
||||
print(f"\n🎯 Запуск обучения через HuggingFace Trainer...")
|
||||
|
||||
train_result = trainer.train()
|
||||
|
||||
# Сохраняем лучшую модель
|
||||
trainer.save_model()
|
||||
hf_tokenizer.save_pretrained(PATHS["hf_model"])
|
||||
|
||||
print("✅ Обучение завершено успешно!")
|
||||
print(f"📊 Final train loss: {train_result.metrics['train_loss']:.4f}")
|
||||
|
||||
if "eval_loss" in train_result.metrics:
|
||||
print(f"📊 Final eval loss: {train_result.metrics['eval_loss']:.4f}")
|
||||
|
||||
# === Сохранение через hf-proxy ===
|
||||
print(f"\n💾 Сохранение через hf-proxy...")
|
||||
|
||||
from hf_proxy import convert_to_hf_format
|
||||
|
||||
# Сохраняем токенизатор в HF формате
|
||||
hf_tokenizer_dir = PATHS["hf_tokenizer"]
|
||||
hf_tokenizer.save_pretrained(hf_tokenizer_dir)
|
||||
|
||||
# Сохраняем модель через hf-proxy
|
||||
hf_proxy_dir = PATHS["hf_proxy_model"]
|
||||
HFAdapter.save_pretrained(hf_model, hf_proxy_dir, tokenizer=hf_tokenizer)
|
||||
|
||||
print(f"✅ Модель сохранена в HF формате:")
|
||||
print(f" - {PATHS['hf_model']}: стандартный HF формат")
|
||||
print(f" - {hf_proxy_dir}: через hf-proxy")
|
||||
print(f" - {hf_tokenizer_dir}: токенизатор в HF формате")
|
||||
|
||||
# === Тестирование генерации ===
|
||||
print(f"\n🧪 Тестирование генерации после обучения...")
|
||||
hf_model.eval()
|
||||
|
||||
for prompt in TEST_PROMPTS[:3]:
|
||||
print(f"\n🔤 Промпт: '{prompt}'")
|
||||
|
||||
try:
|
||||
inputs = hf_tokenizer(prompt, return_tensors="pt")
|
||||
|
||||
with torch.no_grad():
|
||||
generated = hf_model.generate(
|
||||
input_ids=inputs['input_ids'],
|
||||
max_new_tokens=20,
|
||||
do_sample=True,
|
||||
temperature=0.8
|
||||
)
|
||||
|
||||
generated_text = hf_tokenizer.decode(generated[0], skip_special_tokens=True)
|
||||
print(f"🎯 Результат: '{generated_text}'")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка генерации: {e}")
|
||||
|
||||
# === Сохранение результатов ===
|
||||
results = {
|
||||
"experiment": experiment_name,
|
||||
"model_config": model_config,
|
||||
"training_config": TRAINING_CONFIG,
|
||||
"final_loss": train_result.metrics.get('train_loss', 'N/A'),
|
||||
"eval_loss": train_result.metrics.get('eval_loss', 'N/A')
|
||||
}
|
||||
|
||||
logger.save_logs("checkpoints/hf_integration_training_logs.json")
|
||||
|
||||
print(f"\n🎉 Эксперимент с HF интеграцией завершен успешно!")
|
||||
print(f"\n💡 Для использования обученной модели:")
|
||||
print(f" uv run python experiments/hf_integration/generate_with_hf_tools.py")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка в эксперименте: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
313
experiments/llm_only/generate_gpt_bpe.py
Normal file
313
experiments/llm_only/generate_gpt_bpe.py
Normal file
@@ -0,0 +1,313 @@
|
||||
#!/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()
|
||||
231
experiments/llm_only/train_gpt_bpe.py
Normal file
231
experiments/llm_only/train_gpt_bpe.py
Normal file
@@ -0,0 +1,231 @@
|
||||
#!/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()
|
||||
100
experiments/shared/configs.py
Normal file
100
experiments/shared/configs.py
Normal file
@@ -0,0 +1,100 @@
|
||||
"""
|
||||
Общие конфигурации для экспериментов.
|
||||
"""
|
||||
|
||||
# === Данные для обучения ===
|
||||
TRAIN_TEXTS = [
|
||||
"Мир программирования прекрасен и удивителен.",
|
||||
"GPT модели учатся предсказывать следующий токен в последовательности.",
|
||||
"Трансформеры революционно изменили обработку естественного языка.",
|
||||
"Обучение больших языковых моделей требует значительных вычислительных ресурсов и больших объемов данных.",
|
||||
"Искусственный интеллект продолжает развиваться стремительными темпами.",
|
||||
"Глубокое обучение позволяет решать сложные задачи компьютерного зрения.",
|
||||
"Нейронные сети имитируют работу человеческого мозга.",
|
||||
"Машинное обучение находит применение в различных областях науки и техники.",
|
||||
"Python является одним из самых популярных языков программирования для анализа данных.",
|
||||
"Обработка естественного языка позволяет компьютерам понимать человеческую речь.",
|
||||
"Рекуррентные нейронные сети хорошо подходят для работы с последовательностями.",
|
||||
"Сверточные нейронные сети эффективны для обработки изображений.",
|
||||
"Обучение с подкреплением используется для создания игровых ИИ.",
|
||||
"Генеративные состязательные сети могут создавать реалистичные изображения.",
|
||||
"Автоэнкодеры используются для сжатия данных и обучения представлений.",
|
||||
]
|
||||
|
||||
# === Конфигурации моделей ===
|
||||
|
||||
# Базовая конфигурация GPT
|
||||
BASE_GPT_CONFIG = {
|
||||
"vocab_size": None, # Будет установлен динамически
|
||||
"embed_dim": 256,
|
||||
"num_heads": 4,
|
||||
"num_layers": 4,
|
||||
"max_position_embeddings": 128,
|
||||
"dropout": 0.1
|
||||
}
|
||||
|
||||
# Конфигурация для маленькой модели (быстрое тестирование)
|
||||
SMALL_GPT_CONFIG = {
|
||||
"vocab_size": None,
|
||||
"embed_dim": 128,
|
||||
"num_heads": 2,
|
||||
"num_layers": 2,
|
||||
"max_position_embeddings": 64,
|
||||
"dropout": 0.1
|
||||
}
|
||||
|
||||
# Конфигурация для большой модели (качественное обучение)
|
||||
LARGE_GPT_CONFIG = {
|
||||
"vocab_size": None,
|
||||
"embed_dim": 512,
|
||||
"num_heads": 8,
|
||||
"num_layers": 6,
|
||||
"max_position_embeddings": 256,
|
||||
"dropout": 0.1
|
||||
}
|
||||
|
||||
# === Конфигурации токенизатора ===
|
||||
BPE_CONFIG = {
|
||||
"vocab_size": 1000,
|
||||
"special_tokens": ["<pad>", "<unk>", "<bos>", "<eos>"]
|
||||
}
|
||||
|
||||
# === Конфигурации обучения ===
|
||||
TRAINING_CONFIG = {
|
||||
"learning_rate": 3e-4,
|
||||
"batch_size": 2,
|
||||
"num_epochs": 3,
|
||||
"warmup_steps": 50,
|
||||
"gradient_clip": 1.0
|
||||
}
|
||||
|
||||
# === Конфигурации генерации ===
|
||||
GENERATION_CONFIG = {
|
||||
"max_new_tokens": 50,
|
||||
"temperature": 0.7,
|
||||
"do_sample": True,
|
||||
"top_k": None,
|
||||
"top_p": None
|
||||
}
|
||||
|
||||
# === Пути для сохранения ===
|
||||
PATHS = {
|
||||
"bpe_tokenizer": "checkpoints/bpe_tokenizer.json",
|
||||
"gpt_bpe_model": "checkpoints/gpt-bpe/model.pt",
|
||||
"gpt_bpe_config": "checkpoints/gpt-bpe/config.json",
|
||||
"hf_tokenizer": "checkpoints/hf-bpe-tokenizer",
|
||||
"hf_model": "checkpoints/hf-trained",
|
||||
"hf_proxy_model": "checkpoints/hf-trained-proxy"
|
||||
}
|
||||
|
||||
# === Тестовые промпты ===
|
||||
TEST_PROMPTS = [
|
||||
"Искусственный",
|
||||
"Нейронные",
|
||||
"Машинное",
|
||||
"Глубокое",
|
||||
"Python",
|
||||
"Трансформеры",
|
||||
"Обучение",
|
||||
"Программирование",
|
||||
]
|
||||
162
experiments/shared/data.py
Normal file
162
experiments/shared/data.py
Normal file
@@ -0,0 +1,162 @@
|
||||
"""
|
||||
Общие утилиты для работы с данными в экспериментах.
|
||||
"""
|
||||
|
||||
import os
|
||||
from typing import List, Tuple
|
||||
from .configs import TRAIN_TEXTS, PATHS
|
||||
|
||||
|
||||
def load_training_data(split_ratio: float = 0.8) -> Tuple[List[str], List[str]]:
|
||||
"""
|
||||
Загружает данные для обучения и разделяет на train/validation.
|
||||
|
||||
Args:
|
||||
split_ratio: Доля данных для обучения
|
||||
|
||||
Returns:
|
||||
Tuple: (train_texts, val_texts)
|
||||
"""
|
||||
train_size = int(len(TRAIN_TEXTS) * split_ratio)
|
||||
train_data = TRAIN_TEXTS[:train_size]
|
||||
val_data = TRAIN_TEXTS[train_size:]
|
||||
|
||||
return train_data, val_data
|
||||
|
||||
|
||||
def ensure_directories():
|
||||
"""Создает необходимые директории если они не существуют."""
|
||||
directories = [
|
||||
"checkpoints",
|
||||
"checkpoints/gpt-bpe",
|
||||
"checkpoints/hf-bpe-tokenizer",
|
||||
"checkpoints/hf-trained",
|
||||
"checkpoints/hf-trained-proxy",
|
||||
"logs"
|
||||
]
|
||||
|
||||
for directory in directories:
|
||||
os.makedirs(directory, exist_ok=True)
|
||||
|
||||
|
||||
def get_model_paths(experiment_type: str = "llm_only") -> dict:
|
||||
"""
|
||||
Возвращает пути для конкретного типа эксперимента.
|
||||
|
||||
Args:
|
||||
experiment_type: Тип эксперимента ('llm_only' или 'hf_integration')
|
||||
|
||||
Returns:
|
||||
dict: Словарь с путями
|
||||
"""
|
||||
base_paths = PATHS.copy()
|
||||
|
||||
if experiment_type == "hf_integration":
|
||||
base_paths.update({
|
||||
"model": base_paths["hf_model"],
|
||||
"tokenizer": base_paths["hf_tokenizer"]
|
||||
})
|
||||
else: # llm_only
|
||||
base_paths.update({
|
||||
"model": base_paths["gpt_bpe_model"],
|
||||
"tokenizer": base_paths["bpe_tokenizer"]
|
||||
})
|
||||
|
||||
return base_paths
|
||||
|
||||
|
||||
def print_experiment_info(experiment_name: str, config: dict):
|
||||
"""
|
||||
Выводит информацию о запускаемом эксперименте.
|
||||
|
||||
Args:
|
||||
experiment_name: Название эксперимента
|
||||
config: Конфигурация эксперимента
|
||||
"""
|
||||
print("=" * 70)
|
||||
print(f"🚀 Эксперимент: {experiment_name}")
|
||||
print("=" * 70)
|
||||
print("📊 Конфигурация:")
|
||||
for key, value in config.items():
|
||||
print(f" {key}: {value}")
|
||||
print()
|
||||
|
||||
|
||||
def save_experiment_results(results: dict, filepath: str):
|
||||
"""
|
||||
Сохраняет результаты эксперимента в файл.
|
||||
|
||||
Args:
|
||||
results: Словарь с результатами
|
||||
filepath: Путь для сохранения
|
||||
"""
|
||||
import json
|
||||
|
||||
with open(filepath, 'w', encoding='utf-8') as f:
|
||||
json.dump(results, f, ensure_ascii=False, indent=2)
|
||||
|
||||
print(f"✅ Результаты эксперимента сохранены: {filepath}")
|
||||
|
||||
|
||||
def load_experiment_results(filepath: str) -> dict:
|
||||
"""
|
||||
Загружает результаты эксперимента из файла.
|
||||
|
||||
Args:
|
||||
filepath: Путь к файлу с результатами
|
||||
|
||||
Returns:
|
||||
dict: Загруженные результаты
|
||||
"""
|
||||
import json
|
||||
|
||||
if not os.path.exists(filepath):
|
||||
return {}
|
||||
|
||||
with open(filepath, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
class ExperimentLogger:
|
||||
"""
|
||||
Логгер для экспериментов.
|
||||
"""
|
||||
|
||||
def __init__(self, experiment_name: str):
|
||||
self.experiment_name = experiment_name
|
||||
self.metrics = {}
|
||||
|
||||
def log_metric(self, name: str, value: float):
|
||||
"""Логирует метрику."""
|
||||
if name not in self.metrics:
|
||||
self.metrics[name] = []
|
||||
self.metrics[name].append(value)
|
||||
print(f"📈 {name}: {value:.4f}")
|
||||
|
||||
def log_step(self, step: int, loss: float, **kwargs):
|
||||
"""Логирует шаг обучения."""
|
||||
print(f"📊 Step {step}: loss={loss:.4f}", end="")
|
||||
for key, value in kwargs.items():
|
||||
print(f", {key}={value:.4f}", end="")
|
||||
print()
|
||||
|
||||
def log_epoch(self, epoch: int, train_loss: float, val_loss: float = None):
|
||||
"""Логирует завершение эпохи."""
|
||||
print(f"🎯 Epoch {epoch}: train_loss={train_loss:.4f}", end="")
|
||||
if val_loss is not None:
|
||||
print(f", val_loss={val_loss:.4f}", end="")
|
||||
print()
|
||||
|
||||
def save_logs(self, filepath: str):
|
||||
"""Сохраняет логи эксперимента."""
|
||||
import json
|
||||
|
||||
logs = {
|
||||
"experiment_name": self.experiment_name,
|
||||
"metrics": self.metrics
|
||||
}
|
||||
|
||||
with open(filepath, 'w', encoding='utf-8') as f:
|
||||
json.dump(logs, f, ensure_ascii=False, indent=2)
|
||||
|
||||
print(f"✅ Логи эксперимента сохранены: {filepath}")
|
||||
Reference in New Issue
Block a user