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:
Sergey Penkovsky
2025-10-04 22:40:21 +03:00
commit ec07546ea8
54 changed files with 9337 additions and 0 deletions

View 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()