mirror of
https://github.com/pese-git/llm-arch-research.git
synced 2026-01-23 13:00:54 +00:00
303 lines
10 KiB
Python
303 lines
10 KiB
Python
"""
|
||
Утилиты для работы с адаптером 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
|