mirror of
https://github.com/pese-git/llm-arch-research.git
synced 2026-01-24 05:21:16 +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:
325
hf-proxy/src/hf_proxy/hf_utils.py
Normal file
325
hf-proxy/src/hf_proxy/hf_utils.py
Normal file
@@ -0,0 +1,325 @@
|
||||
"""
|
||||
Утилиты для работы с адаптером HuggingFace.
|
||||
"""
|
||||
|
||||
import torch
|
||||
import json
|
||||
from typing import Dict, Any, Optional, List
|
||||
from transformers import AutoTokenizer, AutoConfig
|
||||
from .hf_config import HFAdapterConfig, HFPretrainedConfig
|
||||
from .hf_adapter import HFAdapter, HFGPTAdapter
|
||||
|
||||
|
||||
class HFUtils:
|
||||
"""
|
||||
Утилиты для работы с HuggingFace адаптером.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def create_hf_config_from_llm(llm_config: Dict[str, Any]) -> HFPretrainedConfig:
|
||||
"""
|
||||
Создает конфигурацию HuggingFace из конфигурации llm.
|
||||
|
||||
Args:
|
||||
llm_config: Конфигурация модели из библиотеки llm
|
||||
|
||||
Returns:
|
||||
HFPretrainedConfig: Конфигурация для HuggingFace
|
||||
"""
|
||||
adapter_config = HFAdapterConfig.from_llm_config(llm_config)
|
||||
return HFPretrainedConfig(**adapter_config.to_dict())
|
||||
|
||||
@staticmethod
|
||||
def convert_to_hf_format(
|
||||
llm_model,
|
||||
tokenizer = None,
|
||||
model_name: str = "custom-gpt"
|
||||
) -> tuple:
|
||||
"""
|
||||
Конвертирует llm модель в формат HuggingFace.
|
||||
|
||||
Args:
|
||||
llm_model: Модель из библиотеки llm
|
||||
tokenizer: Токенизатор (HF или кастомный)
|
||||
model_name: Имя модели для сохранения
|
||||
|
||||
Returns:
|
||||
tuple: (адаптированная модель, токенизатор)
|
||||
"""
|
||||
# Создаем адаптер
|
||||
hf_model = HFAdapter.from_llm_model(llm_model)
|
||||
|
||||
# Если токенизатор не передан, создаем стандартный
|
||||
if tokenizer is None:
|
||||
from transformers import AutoTokenizer
|
||||
tokenizer = AutoTokenizer.from_pretrained("gpt2")
|
||||
# Устанавливаем специальные токены
|
||||
if tokenizer.pad_token is None:
|
||||
tokenizer.pad_token = tokenizer.eos_token
|
||||
elif hasattr(tokenizer, '__class__') and 'BPETokenizer' in str(tokenizer.__class__):
|
||||
# Если передан наш кастомный токенизатор, создаем адаптер
|
||||
from .hf_tokenizer import create_hf_tokenizer
|
||||
tokenizer = create_hf_tokenizer(tokenizer)
|
||||
|
||||
return hf_model, tokenizer
|
||||
|
||||
@staticmethod
|
||||
def push_to_hub(
|
||||
model: HFGPTAdapter,
|
||||
tokenizer,
|
||||
repo_name: str,
|
||||
organization: Optional[str] = None,
|
||||
private: bool = False,
|
||||
**kwargs
|
||||
):
|
||||
"""
|
||||
Загружает модель в HuggingFace Hub.
|
||||
|
||||
Args:
|
||||
model: Адаптированная модель
|
||||
tokenizer: Токенизатор
|
||||
repo_name: Имя репозитория
|
||||
organization: Организация (опционально)
|
||||
private: Приватный репозиторий
|
||||
**kwargs: Дополнительные параметры
|
||||
"""
|
||||
try:
|
||||
from huggingface_hub import HfApi, ModelCard, create_repo
|
||||
|
||||
# Создаем репозиторий
|
||||
if organization:
|
||||
repo_id = f"{organization}/{repo_name}"
|
||||
else:
|
||||
repo_id = repo_name
|
||||
|
||||
create_repo(repo_id, private=private, exist_ok=True)
|
||||
|
||||
# Сохраняем модель локально
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
# Сохраняем модель
|
||||
HFAdapter.save_pretrained(model, tmp_dir, tokenizer=tokenizer)
|
||||
|
||||
# Создаем Model Card
|
||||
card = ModelCard.from_template(
|
||||
model_name=repo_name,
|
||||
language="ru",
|
||||
license="apache-2.0",
|
||||
tags=["llm", "gpt", "custom"],
|
||||
)
|
||||
card.save(os.path.join(tmp_dir, "README.md"))
|
||||
|
||||
# Загружаем в Hub
|
||||
api = HfApi()
|
||||
api.upload_folder(
|
||||
folder_path=tmp_dir,
|
||||
repo_id=repo_id,
|
||||
commit_message="Initial commit with custom GPT model"
|
||||
)
|
||||
|
||||
print(f"✅ Модель успешно загружена в HuggingFace Hub: {repo_id}")
|
||||
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Для загрузки в HuggingFace Hub установите huggingface_hub: "
|
||||
"pip install huggingface_hub"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def load_from_hub(
|
||||
repo_id: str,
|
||||
**kwargs
|
||||
) -> tuple:
|
||||
"""
|
||||
Загружает модель из HuggingFace Hub.
|
||||
|
||||
Args:
|
||||
repo_id: ID репозитория
|
||||
**kwargs: Дополнительные параметры
|
||||
|
||||
Returns:
|
||||
tuple: (модель, токенизатор)
|
||||
"""
|
||||
from transformers import AutoTokenizer
|
||||
|
||||
# Загружаем токенизатор
|
||||
tokenizer = AutoTokenizer.from_pretrained(repo_id, **kwargs)
|
||||
|
||||
# Загружаем конфигурацию
|
||||
config = AutoConfig.from_pretrained(repo_id, **kwargs)
|
||||
|
||||
# Создаем модель llm на основе конфигурации
|
||||
llm_config = {
|
||||
"vocab_size": config.vocab_size,
|
||||
"embed_dim": config.hidden_size,
|
||||
"num_heads": config.num_attention_heads,
|
||||
"num_layers": config.num_hidden_layers,
|
||||
"max_position_embeddings": config.max_position_embeddings,
|
||||
"dropout": config.hidden_dropout_prob,
|
||||
}
|
||||
|
||||
# Загружаем модель через адаптер
|
||||
model = HFAdapter.from_pretrained(
|
||||
f"{repo_id}/pytorch_model.bin",
|
||||
HFAdapterConfig.from_llm_config(llm_config)
|
||||
)
|
||||
|
||||
return model, tokenizer
|
||||
|
||||
@staticmethod
|
||||
def compare_with_hf_model(
|
||||
llm_model,
|
||||
hf_model_name: str = "gpt2",
|
||||
test_input: str = "Hello world"
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Сравнивает llm модель с эталонной моделью из HuggingFace.
|
||||
|
||||
Args:
|
||||
llm_model: Модель из библиотеки llm
|
||||
hf_model_name: Имя модели HuggingFace для сравнения
|
||||
test_input: Тестовый вход
|
||||
|
||||
Returns:
|
||||
Dict: Результаты сравнения
|
||||
"""
|
||||
from transformers import AutoModelForCausalLM, AutoTokenizer
|
||||
|
||||
# Загружаем эталонную модель
|
||||
hf_tokenizer = AutoTokenizer.from_pretrained(hf_model_name)
|
||||
hf_model = AutoModelForCausalLM.from_pretrained(hf_model_name)
|
||||
|
||||
# Подготавливаем входные данные
|
||||
inputs = hf_tokenizer(test_input, return_tensors="pt")
|
||||
|
||||
# Получаем логиты от обеих моделей
|
||||
with torch.no_grad():
|
||||
hf_logits = hf_model(**inputs).logits
|
||||
llm_logits = llm_model(inputs['input_ids'])
|
||||
|
||||
# Сравниваем результаты
|
||||
hf_probs = torch.softmax(hf_logits[0, -1], dim=-1)
|
||||
llm_probs = torch.softmax(llm_logits[0, -1], dim=-1)
|
||||
|
||||
# Вычисляем метрики
|
||||
kl_divergence = torch.nn.functional.kl_div(
|
||||
torch.log(llm_probs + 1e-8),
|
||||
hf_probs,
|
||||
reduction='batchmean'
|
||||
)
|
||||
|
||||
cosine_similarity = torch.nn.functional.cosine_similarity(
|
||||
hf_logits.flatten(),
|
||||
llm_logits.flatten(),
|
||||
dim=0
|
||||
)
|
||||
|
||||
return {
|
||||
"kl_divergence": kl_divergence.item(),
|
||||
"cosine_similarity": cosine_similarity.item(),
|
||||
"hf_top_tokens": torch.topk(hf_probs, 5).indices.tolist(),
|
||||
"llm_top_tokens": torch.topk(llm_probs, 5).indices.tolist(),
|
||||
}
|
||||
|
||||
|
||||
class TokenizerWrapper:
|
||||
"""
|
||||
Обертка для токенизатора с дополнительными утилитами.
|
||||
"""
|
||||
|
||||
def __init__(self, tokenizer):
|
||||
self.tokenizer = tokenizer
|
||||
|
||||
def encode_batch(self, texts: List[str], **kwargs) -> Dict[str, torch.Tensor]:
|
||||
"""
|
||||
Кодирует батч текстов.
|
||||
|
||||
Args:
|
||||
texts: Список текстов
|
||||
**kwargs: Дополнительные параметры токенизации
|
||||
|
||||
Returns:
|
||||
Dict: Токенизированные данные
|
||||
"""
|
||||
return self.tokenizer(
|
||||
texts,
|
||||
padding=True,
|
||||
truncation=True,
|
||||
return_tensors="pt",
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def decode_batch(self, token_ids: torch.Tensor, **kwargs) -> List[str]:
|
||||
"""
|
||||
Декодирует батч токенов.
|
||||
|
||||
Args:
|
||||
token_ids: Тензор с токенами
|
||||
**kwargs: Дополнительные параметры декодирования
|
||||
|
||||
Returns:
|
||||
List[str]: Декодированные тексты
|
||||
"""
|
||||
if token_ids.dim() == 1:
|
||||
token_ids = token_ids.unsqueeze(0)
|
||||
|
||||
texts = []
|
||||
for i in range(token_ids.size(0)):
|
||||
text = self.tokenizer.decode(
|
||||
token_ids[i],
|
||||
skip_special_tokens=True,
|
||||
**kwargs
|
||||
)
|
||||
texts.append(text)
|
||||
|
||||
return texts
|
||||
|
||||
def get_vocab_size(self) -> int:
|
||||
"""Возвращает размер словаря."""
|
||||
return len(self.tokenizer)
|
||||
|
||||
def get_special_tokens(self) -> Dict[str, int]:
|
||||
"""Возвращает специальные токены."""
|
||||
return {
|
||||
"pad_token": self.tokenizer.pad_token_id,
|
||||
"eos_token": self.tokenizer.eos_token_id,
|
||||
"bos_token": self.tokenizer.bos_token_id,
|
||||
"unk_token": self.tokenizer.unk_token_id,
|
||||
}
|
||||
|
||||
|
||||
def create_hf_pipeline(
|
||||
llm_model,
|
||||
tokenizer=None,
|
||||
device: str = "auto",
|
||||
**kwargs
|
||||
):
|
||||
"""
|
||||
Создает HuggingFace pipeline из llm модели.
|
||||
|
||||
Args:
|
||||
llm_model: Модель из библиотеки llm
|
||||
tokenizer: Токенизатор
|
||||
device: Устройство для вычислений
|
||||
**kwargs: Дополнительные параметры pipeline
|
||||
|
||||
Returns:
|
||||
transformers.Pipeline: Готовый pipeline
|
||||
"""
|
||||
from transformers import pipeline
|
||||
|
||||
# Конвертируем модель в HF формат
|
||||
hf_model, tokenizer = HFUtils.convert_to_hf_format(llm_model, tokenizer)
|
||||
|
||||
# Создаем pipeline
|
||||
pipe = pipeline(
|
||||
"text-generation",
|
||||
model=hf_model,
|
||||
tokenizer=tokenizer,
|
||||
device=device,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
return pipe
|
||||
Reference in New Issue
Block a user