mirror of
https://github.com/pese-git/llm-arch-research.git
synced 2026-01-23 21:10:54 +00:00
Рефакторинг: единообразие оформления кода (пробелы, кавычки, пустые строки), без изменения логики по всему проекту.
This commit is contained in:
@@ -27,16 +27,13 @@ __all__ = [
|
||||
# Основные классы адаптера
|
||||
"HFAdapter",
|
||||
"HFGPTAdapter",
|
||||
|
||||
# Конфигурации
|
||||
"HFAdapterConfig",
|
||||
"HFAdapterConfig",
|
||||
"HFPretrainedConfig",
|
||||
|
||||
# Адаптеры токенизаторов
|
||||
"HFTokenizerAdapter",
|
||||
"create_hf_tokenizer",
|
||||
"create_hf_tokenizer",
|
||||
"convert_to_hf_format",
|
||||
|
||||
# Утилиты
|
||||
"HFUtils",
|
||||
"TokenizerWrapper",
|
||||
|
||||
@@ -6,12 +6,12 @@ import torch
|
||||
import torch.nn as nn
|
||||
from typing import Optional, Tuple, Union, List
|
||||
from transformers import (
|
||||
PreTrainedModel,
|
||||
PreTrainedModel,
|
||||
GPT2LMHeadModel,
|
||||
GPT2Config,
|
||||
GenerationConfig,
|
||||
LogitsProcessorList,
|
||||
StoppingCriteriaList
|
||||
StoppingCriteriaList,
|
||||
)
|
||||
from transformers.modeling_outputs import CausalLMOutputWithCrossAttentions
|
||||
|
||||
@@ -24,38 +24,39 @@ class HFGPTAdapter(PreTrainedModel):
|
||||
Адаптер для модели GPT из библиотеки llm.
|
||||
Позволяет использовать кастомные GPT модели с HuggingFace Transformers.
|
||||
"""
|
||||
|
||||
config_class = HFPretrainedConfig
|
||||
|
||||
|
||||
def __init__(self, config: HFPretrainedConfig, llm_model: Optional[GPT] = None):
|
||||
"""
|
||||
Инициализация адаптера.
|
||||
|
||||
|
||||
Args:
|
||||
config: Конфигурация HuggingFace
|
||||
llm_model: Опционально, предварительно созданная модель llm
|
||||
"""
|
||||
super().__init__(config)
|
||||
|
||||
|
||||
# Преобразуем HF конфигурацию в формат llm
|
||||
llm_config = self._hf_to_llm_config(config)
|
||||
|
||||
|
||||
# Создаем или используем переданную модель
|
||||
if llm_model is None:
|
||||
self.llm_model = GPT(llm_config)
|
||||
else:
|
||||
self.llm_model = llm_model
|
||||
|
||||
|
||||
# Устанавливаем веса если они есть в конфигурации
|
||||
if hasattr(config, 'state_dict') and config.state_dict is not None:
|
||||
if hasattr(config, "state_dict") and config.state_dict is not None:
|
||||
self.llm_model.load_state_dict(config.state_dict)
|
||||
|
||||
|
||||
def _hf_to_llm_config(self, hf_config: HFPretrainedConfig) -> dict:
|
||||
"""
|
||||
Преобразует конфигурацию HF в формат llm.
|
||||
|
||||
|
||||
Args:
|
||||
hf_config: Конфигурация HuggingFace
|
||||
|
||||
|
||||
Returns:
|
||||
dict: Конфигурация для llm модели
|
||||
"""
|
||||
@@ -67,7 +68,7 @@ class HFGPTAdapter(PreTrainedModel):
|
||||
"max_position_embeddings": hf_config.max_position_embeddings,
|
||||
"dropout": hf_config.hidden_dropout_prob,
|
||||
}
|
||||
|
||||
|
||||
def forward(
|
||||
self,
|
||||
input_ids: Optional[torch.Tensor] = None,
|
||||
@@ -78,11 +79,11 @@ class HFGPTAdapter(PreTrainedModel):
|
||||
output_attentions: Optional[bool] = None,
|
||||
output_hidden_states: Optional[bool] = None,
|
||||
return_dict: Optional[bool] = None,
|
||||
**kwargs
|
||||
**kwargs,
|
||||
) -> Union[Tuple, CausalLMOutputWithCrossAttentions]:
|
||||
"""
|
||||
Прямой проход модели.
|
||||
|
||||
|
||||
Args:
|
||||
input_ids: Входные токены [batch_size, seq_len]
|
||||
attention_mask: Маска внимания [batch_size, seq_len]
|
||||
@@ -92,38 +93,39 @@ class HFGPTAdapter(PreTrainedModel):
|
||||
output_attentions: Возвращать веса внимания
|
||||
output_hidden_states: Возвращать скрытые состояния
|
||||
return_dict: Возвращать словарь вместо кортежа
|
||||
|
||||
|
||||
Returns:
|
||||
CausalLMOutputWithCrossAttentions или кортеж
|
||||
"""
|
||||
return_dict = return_dict if return_dict is not None else self.config.use_return_dict
|
||||
|
||||
return_dict = (
|
||||
return_dict if return_dict is not None else self.config.use_return_dict
|
||||
)
|
||||
|
||||
# Основной forward pass
|
||||
outputs = self.llm_model(input_ids)
|
||||
if isinstance(outputs, tuple):
|
||||
logits = outputs[0]
|
||||
else:
|
||||
logits = outputs
|
||||
|
||||
|
||||
loss = None
|
||||
if labels is not None:
|
||||
# Сдвигаем логиты и метки для языкового моделирования
|
||||
shift_logits = logits[..., :-1, :].contiguous()
|
||||
shift_labels = labels[..., 1:].contiguous()
|
||||
|
||||
|
||||
# Вычисляем cross-entropy loss
|
||||
loss_fct = nn.CrossEntropyLoss()
|
||||
loss = loss_fct(
|
||||
shift_logits.view(-1, shift_logits.size(-1)),
|
||||
shift_labels.view(-1)
|
||||
shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1)
|
||||
)
|
||||
|
||||
|
||||
if not return_dict:
|
||||
output = (logits,)
|
||||
if loss is not None:
|
||||
output = (loss,) + output
|
||||
return output
|
||||
|
||||
|
||||
return CausalLMOutputWithCrossAttentions(
|
||||
loss=loss,
|
||||
logits=logits,
|
||||
@@ -132,30 +134,27 @@ class HFGPTAdapter(PreTrainedModel):
|
||||
attentions=None,
|
||||
cross_attentions=None,
|
||||
)
|
||||
|
||||
|
||||
def prepare_inputs_for_generation(
|
||||
self,
|
||||
input_ids: torch.Tensor,
|
||||
past_key_values: Optional[Tuple] = None,
|
||||
**kwargs
|
||||
self, input_ids: torch.Tensor, past_key_values: Optional[Tuple] = None, **kwargs
|
||||
) -> dict:
|
||||
"""
|
||||
Подготавливает входные данные для генерации.
|
||||
|
||||
|
||||
Args:
|
||||
input_ids: Входные токены
|
||||
past_key_values: Кешированные ключи и значения
|
||||
|
||||
|
||||
Returns:
|
||||
dict: Подготовленные входные данные
|
||||
"""
|
||||
# Наша простая реализация пока не поддерживает past_key_values
|
||||
return {"input_ids": input_ids}
|
||||
|
||||
|
||||
def can_generate(self) -> bool:
|
||||
"""Проверяет, может ли модель генерировать текст."""
|
||||
return True
|
||||
|
||||
|
||||
def generate(
|
||||
self,
|
||||
input_ids: Optional[torch.Tensor] = None,
|
||||
@@ -163,32 +162,32 @@ class HFGPTAdapter(PreTrainedModel):
|
||||
generation_config: Optional[GenerationConfig] = None,
|
||||
logits_processor: Optional[LogitsProcessorList] = None,
|
||||
stopping_criteria: Optional[StoppingCriteriaList] = None,
|
||||
**kwargs
|
||||
**kwargs,
|
||||
) -> torch.Tensor:
|
||||
"""
|
||||
Генерация текста с поддержкой HuggingFace интерфейса.
|
||||
|
||||
|
||||
Args:
|
||||
input_ids: Входные токены
|
||||
attention_mask: Маска внимания
|
||||
generation_config: Конфигурация генерации
|
||||
logits_processor: Процессоры логитов
|
||||
stopping_criteria: Критерии остановки
|
||||
|
||||
|
||||
Returns:
|
||||
torch.Tensor: Сгенерированные токены
|
||||
"""
|
||||
# Извлекаем обязательные параметры из kwargs или используем значения по умолчанию
|
||||
max_new_tokens = kwargs.pop('max_new_tokens', 50)
|
||||
do_sample = kwargs.pop('do_sample', True)
|
||||
|
||||
max_new_tokens = kwargs.pop("max_new_tokens", 50)
|
||||
do_sample = kwargs.pop("do_sample", True)
|
||||
|
||||
# Используем встроенную генерацию llm модели
|
||||
return self.llm_model.generate(
|
||||
x=input_ids,
|
||||
max_new_tokens=max_new_tokens,
|
||||
do_sample=do_sample,
|
||||
attention_mask=attention_mask,
|
||||
**kwargs
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
@@ -196,64 +195,66 @@ class HFAdapter:
|
||||
"""
|
||||
Основной класс адаптера для преобразования моделей llm в формат HuggingFace.
|
||||
"""
|
||||
|
||||
|
||||
@staticmethod
|
||||
def from_llm_model(
|
||||
llm_model: GPT,
|
||||
hf_config: Optional[HFAdapterConfig] = None
|
||||
llm_model: GPT, hf_config: Optional[HFAdapterConfig] = None
|
||||
) -> HFGPTAdapter:
|
||||
"""
|
||||
Создает адаптер из существующей llm модели.
|
||||
|
||||
|
||||
Args:
|
||||
llm_model: Обученная модель из библиотеки llm
|
||||
hf_config: Конфигурация для HuggingFace
|
||||
|
||||
|
||||
Returns:
|
||||
HFGPTAdapter: Адаптированная модель
|
||||
"""
|
||||
if hf_config is None:
|
||||
# Создаем конфигурацию из модели llm
|
||||
hf_config = HFAdapterConfig.from_llm_config(llm_model.config)
|
||||
|
||||
|
||||
# Преобразуем в PretrainedConfig
|
||||
pretrained_config = HFPretrainedConfig(**hf_config.to_dict())
|
||||
|
||||
|
||||
return HFGPTAdapter(pretrained_config, llm_model)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def from_pretrained(
|
||||
model_path: str,
|
||||
hf_config: Optional[HFAdapterConfig] = None
|
||||
model_path: str, hf_config: Optional[HFAdapterConfig] = None
|
||||
) -> HFGPTAdapter:
|
||||
"""
|
||||
Загружает модель из чекпоинта и создает адаптер.
|
||||
|
||||
|
||||
Args:
|
||||
model_path: Путь к сохраненной модели
|
||||
hf_config: Конфигурация для HuggingFace
|
||||
|
||||
|
||||
Returns:
|
||||
HFGPTAdapter: Адаптированная модель
|
||||
"""
|
||||
# Загружаем состояние модели
|
||||
state_dict = torch.load(model_path, map_location='cpu')
|
||||
|
||||
state_dict = torch.load(model_path, map_location="cpu")
|
||||
|
||||
# Определяем конфигурацию из состояния модели или используем переданную
|
||||
if hf_config is None:
|
||||
# Пытаемся определить конфигурацию из состояния модели
|
||||
# Это упрощенный подход - в реальности нужно сохранять конфигурацию отдельно
|
||||
vocab_size = state_dict.get('_token_embeddings._embedding.weight', torch.zeros(50257, 768)).shape[0]
|
||||
embed_dim = state_dict.get('_token_embeddings._embedding.weight', torch.zeros(50257, 768)).shape[1]
|
||||
|
||||
vocab_size = state_dict.get(
|
||||
"_token_embeddings._embedding.weight", torch.zeros(50257, 768)
|
||||
).shape[0]
|
||||
embed_dim = state_dict.get(
|
||||
"_token_embeddings._embedding.weight", torch.zeros(50257, 768)
|
||||
).shape[1]
|
||||
|
||||
hf_config = HFAdapterConfig(
|
||||
vocab_size=vocab_size,
|
||||
hidden_size=embed_dim,
|
||||
# Остальные параметры можно установить по умолчанию
|
||||
)
|
||||
|
||||
|
||||
pretrained_config = HFPretrainedConfig(**hf_config.to_dict())
|
||||
|
||||
|
||||
# Создаем модель llm и загружаем веса
|
||||
llm_config = {
|
||||
"vocab_size": hf_config.vocab_size,
|
||||
@@ -263,21 +264,17 @@ class HFAdapter:
|
||||
"max_position_embeddings": hf_config.max_position_embeddings,
|
||||
"dropout": hf_config.hidden_dropout_prob,
|
||||
}
|
||||
|
||||
|
||||
llm_model = GPT(llm_config)
|
||||
llm_model.load_state_dict(state_dict)
|
||||
|
||||
|
||||
return HFGPTAdapter(pretrained_config, llm_model)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def save_pretrained(
|
||||
model: HFGPTAdapter,
|
||||
save_directory: str,
|
||||
**kwargs
|
||||
):
|
||||
def save_pretrained(model: HFGPTAdapter, save_directory: str, **kwargs):
|
||||
"""
|
||||
Сохраняет адаптированную модель в формате HuggingFace.
|
||||
|
||||
|
||||
Args:
|
||||
model: Адаптированная модель
|
||||
save_directory: Директория для сохранения
|
||||
@@ -285,19 +282,19 @@ class HFAdapter:
|
||||
"""
|
||||
import os
|
||||
import json
|
||||
|
||||
|
||||
# Создаем директорию если не существует
|
||||
os.makedirs(save_directory, exist_ok=True)
|
||||
|
||||
|
||||
# Сохраняем конфигурацию
|
||||
config_path = os.path.join(save_directory, "config.json")
|
||||
with open(config_path, 'w', encoding='utf-8') as f:
|
||||
with open(config_path, "w", encoding="utf-8") as f:
|
||||
json.dump(model.config.to_dict(), f, indent=2, ensure_ascii=False)
|
||||
|
||||
|
||||
# Сохраняем веса модели
|
||||
model_path = os.path.join(save_directory, "pytorch_model.bin")
|
||||
torch.save(model.llm_model.state_dict(), model_path)
|
||||
|
||||
|
||||
# Сохраняем токенизатор если передан
|
||||
if hasattr(kwargs, 'tokenizer') and kwargs['tokenizer'] is not None:
|
||||
kwargs['tokenizer'].save_pretrained(save_directory)
|
||||
if hasattr(kwargs, "tokenizer") and kwargs["tokenizer"] is not None:
|
||||
kwargs["tokenizer"].save_pretrained(save_directory)
|
||||
|
||||
@@ -6,11 +6,12 @@ from dataclasses import dataclass, field
|
||||
from typing import Dict, Any, Optional
|
||||
from transformers import PretrainedConfig
|
||||
|
||||
|
||||
@dataclass
|
||||
class HFAdapterConfig:
|
||||
"""
|
||||
Конфигурация для адаптера HuggingFace.
|
||||
|
||||
|
||||
Параметры:
|
||||
model_type: Тип модели (gpt, llama, etc.)
|
||||
vocab_size: Размер словаря
|
||||
@@ -28,6 +29,7 @@ class HFAdapterConfig:
|
||||
eos_token_id: ID токена конца строки
|
||||
bos_token_id: ID токена начала строки
|
||||
"""
|
||||
|
||||
model_type: str = "gpt"
|
||||
vocab_size: int = 50257
|
||||
hidden_size: int = 768
|
||||
@@ -43,49 +45,50 @@ class HFAdapterConfig:
|
||||
pad_token_id: int = 50256
|
||||
eos_token_id: int = 50256
|
||||
bos_token_id: int = 50256
|
||||
|
||||
|
||||
# Дополнительные параметры для совместимости
|
||||
architectures: list = field(default_factory=lambda: ["GPT2LMHeadModel"])
|
||||
torch_dtype: str = "float32"
|
||||
transformers_version: str = "4.44.0"
|
||||
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Преобразует конфигурацию в словарь."""
|
||||
return {
|
||||
k: v for k, v in self.__dict__.items()
|
||||
if not k.startswith('_') and not callable(v)
|
||||
k: v
|
||||
for k, v in self.__dict__.items()
|
||||
if not k.startswith("_") and not callable(v)
|
||||
}
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_llm_config(cls, llm_config: Dict[str, Any]) -> "HFAdapterConfig":
|
||||
"""
|
||||
Создает конфигурацию HF из конфигурации llm.
|
||||
|
||||
|
||||
Args:
|
||||
llm_config: Конфигурация модели из библиотеки llm
|
||||
|
||||
|
||||
Returns:
|
||||
HFAdapterConfig: Конфигурация для HuggingFace
|
||||
"""
|
||||
# Маппинг параметров из llm в HF формат
|
||||
mapping = {
|
||||
"embed_dim": "hidden_size",
|
||||
"num_layers": "num_hidden_layers",
|
||||
"num_layers": "num_hidden_layers",
|
||||
"num_heads": "num_attention_heads",
|
||||
"max_position_embeddings": "max_position_embeddings",
|
||||
"dropout": "hidden_dropout_prob",
|
||||
"vocab_size": "vocab_size"
|
||||
"vocab_size": "vocab_size",
|
||||
}
|
||||
|
||||
|
||||
hf_config_dict = {}
|
||||
for llm_key, hf_key in mapping.items():
|
||||
if llm_key in llm_config:
|
||||
hf_config_dict[hf_key] = llm_config[llm_key]
|
||||
|
||||
|
||||
# Устанавливаем промежуточный размер (обычно 4x hidden_size)
|
||||
if "hidden_size" in hf_config_dict:
|
||||
hf_config_dict["intermediate_size"] = hf_config_dict["hidden_size"] * 4
|
||||
|
||||
|
||||
return cls(**hf_config_dict)
|
||||
|
||||
|
||||
@@ -94,8 +97,9 @@ class HFPretrainedConfig(PretrainedConfig):
|
||||
Конфигурация для предобученных моделей HuggingFace.
|
||||
Наследуется от PretrainedConfig для полной совместимости.
|
||||
"""
|
||||
|
||||
model_type = "gpt"
|
||||
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
vocab_size=50257,
|
||||
@@ -112,15 +116,15 @@ class HFPretrainedConfig(PretrainedConfig):
|
||||
pad_token_id=50256,
|
||||
eos_token_id=50256,
|
||||
bos_token_id=50256,
|
||||
**kwargs
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(
|
||||
pad_token_id=pad_token_id,
|
||||
eos_token_id=eos_token_id,
|
||||
bos_token_id=bos_token_id,
|
||||
**kwargs
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
self.vocab_size = vocab_size
|
||||
self.hidden_size = hidden_size
|
||||
self.num_hidden_layers = num_hidden_layers
|
||||
|
||||
@@ -12,84 +12,82 @@ class HFTokenizerAdapter:
|
||||
Упрощенный адаптер для кастомных токенизаторов llm.
|
||||
Предоставляет совместимый с HuggingFace интерфейс.
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, llm_tokenizer: BaseTokenizer):
|
||||
"""
|
||||
Инициализация адаптера.
|
||||
|
||||
|
||||
Args:
|
||||
llm_tokenizer: Кастомный токенизатор из llm
|
||||
"""
|
||||
self.llm_tokenizer = llm_tokenizer
|
||||
|
||||
|
||||
# Получаем словарь и размер
|
||||
self._vocab = llm_tokenizer.get_vocab()
|
||||
self.vocab_size = llm_tokenizer.get_vocab_size()
|
||||
|
||||
|
||||
# Устанавливаем специальные токены
|
||||
self.pad_token = getattr(llm_tokenizer, 'pad_token', '<pad>')
|
||||
self.unk_token = getattr(llm_tokenizer, 'unk_token', '<unk>')
|
||||
self.bos_token = getattr(llm_tokenizer, 'bos_token', '<bos>')
|
||||
self.eos_token = getattr(llm_tokenizer, 'eos_token', '<eos>')
|
||||
|
||||
self.pad_token = getattr(llm_tokenizer, "pad_token", "<pad>")
|
||||
self.unk_token = getattr(llm_tokenizer, "unk_token", "<unk>")
|
||||
self.bos_token = getattr(llm_tokenizer, "bos_token", "<bos>")
|
||||
self.eos_token = getattr(llm_tokenizer, "eos_token", "<eos>")
|
||||
|
||||
# Сохраняем ID специальных токенов
|
||||
self.pad_token_id = getattr(llm_tokenizer, 'pad_token_id', 0)
|
||||
self.unk_token_id = getattr(llm_tokenizer, 'unk_token_id', 1)
|
||||
self.bos_token_id = getattr(llm_tokenizer, 'bos_token_id', 2)
|
||||
self.eos_token_id = getattr(llm_tokenizer, 'eos_token_id', 3)
|
||||
|
||||
self.pad_token_id = getattr(llm_tokenizer, "pad_token_id", 0)
|
||||
self.unk_token_id = getattr(llm_tokenizer, "unk_token_id", 1)
|
||||
self.bos_token_id = getattr(llm_tokenizer, "bos_token_id", 2)
|
||||
self.eos_token_id = getattr(llm_tokenizer, "eos_token_id", 3)
|
||||
|
||||
def __call__(self, text: str, **kwargs):
|
||||
"""
|
||||
Вызов токенизатора с параметрами как у HuggingFace.
|
||||
|
||||
|
||||
Args:
|
||||
text: Входной текст
|
||||
**kwargs: Параметры токенизации
|
||||
|
||||
|
||||
Returns:
|
||||
dict: Словарь с токенами
|
||||
"""
|
||||
return_tensors = kwargs.get('return_tensors', None)
|
||||
padding = kwargs.get('padding', False)
|
||||
truncation = kwargs.get('truncation', False)
|
||||
max_length = kwargs.get('max_length', None)
|
||||
add_special_tokens = kwargs.get('add_special_tokens', True)
|
||||
|
||||
return_tensors = kwargs.get("return_tensors", None)
|
||||
padding = kwargs.get("padding", False)
|
||||
truncation = kwargs.get("truncation", False)
|
||||
max_length = kwargs.get("max_length", None)
|
||||
add_special_tokens = kwargs.get("add_special_tokens", True)
|
||||
|
||||
# Кодируем текст
|
||||
#input_ids = self.llm_tokenizer.encode(
|
||||
# text,
|
||||
# input_ids = self.llm_tokenizer.encode(
|
||||
# text,
|
||||
# add_special_tokens=add_special_tokens
|
||||
#)
|
||||
# )
|
||||
if isinstance(text, str):
|
||||
input_ids = self.llm_tokenizer.encode(
|
||||
text,
|
||||
add_special_tokens=add_special_tokens
|
||||
text, add_special_tokens=add_special_tokens
|
||||
)
|
||||
input_ids = [input_ids] # <-- оборачиваем в batch
|
||||
else:
|
||||
# Список строк, батч-режим!
|
||||
input_ids = [
|
||||
self.llm_tokenizer.encode(
|
||||
t,
|
||||
add_special_tokens=add_special_tokens
|
||||
) for t in text
|
||||
self.llm_tokenizer.encode(t, add_special_tokens=add_special_tokens)
|
||||
for t in text
|
||||
]
|
||||
|
||||
|
||||
# Применяем truncation
|
||||
if truncation and max_length is not None and len(input_ids) > max_length:
|
||||
input_ids = input_ids[:max_length]
|
||||
|
||||
|
||||
# Применяем padding
|
||||
if padding and max_length is not None and len(input_ids) < max_length:
|
||||
input_ids = input_ids + [self.pad_token_id] * (max_length - len(input_ids))
|
||||
|
||||
|
||||
# Конвертируем в тензоры если нужно
|
||||
if return_tensors == "pt":
|
||||
import torch
|
||||
|
||||
input_ids = torch.tensor([input_ids])
|
||||
|
||||
|
||||
return {"input_ids": input_ids}
|
||||
|
||||
|
||||
def encode(
|
||||
self,
|
||||
text: str,
|
||||
@@ -99,11 +97,11 @@ class HFTokenizerAdapter:
|
||||
truncation: bool = False,
|
||||
max_length: Optional[int] = None,
|
||||
return_tensors: Optional[str] = None,
|
||||
**kwargs
|
||||
**kwargs,
|
||||
) -> Union[List[int], List[List[int]]]:
|
||||
"""
|
||||
Кодирует текст в последовательность токенов.
|
||||
|
||||
|
||||
Args:
|
||||
text: Входной текст
|
||||
text_pair: Второй текст (для парных задач)
|
||||
@@ -112,84 +110,91 @@ class HFTokenizerAdapter:
|
||||
truncation: Обрезать последовательность
|
||||
max_length: Максимальная длина
|
||||
return_tensors: Возвращать тензоры
|
||||
|
||||
|
||||
Returns:
|
||||
Список токенов или список списков токенов
|
||||
"""
|
||||
# Кодируем основной текст
|
||||
token_ids = self.llm_tokenizer.encode(
|
||||
text,
|
||||
add_special_tokens=add_special_tokens
|
||||
text, add_special_tokens=add_special_tokens
|
||||
)
|
||||
|
||||
|
||||
# Обрабатываем text_pair если есть
|
||||
if text_pair is not None:
|
||||
pair_ids = self.llm_tokenizer.encode(
|
||||
text_pair,
|
||||
add_special_tokens=False
|
||||
)
|
||||
pair_ids = self.llm_tokenizer.encode(text_pair, add_special_tokens=False)
|
||||
token_ids.extend(pair_ids)
|
||||
|
||||
|
||||
# Применяем truncation
|
||||
if truncation and max_length is not None and len(token_ids) > max_length:
|
||||
token_ids = token_ids[:max_length]
|
||||
|
||||
|
||||
# Применяем padding
|
||||
if padding and max_length is not None and len(token_ids) < max_length:
|
||||
token_ids = token_ids + [self.pad_token_id] * (max_length - len(token_ids))
|
||||
|
||||
|
||||
# Конвертируем в тензоры если нужно
|
||||
if return_tensors == "pt":
|
||||
import torch
|
||||
|
||||
return torch.tensor([token_ids])
|
||||
elif return_tensors == "np":
|
||||
import numpy as np
|
||||
|
||||
return np.array([token_ids])
|
||||
|
||||
|
||||
return token_ids
|
||||
|
||||
|
||||
def decode(
|
||||
self,
|
||||
token_ids: Union[int, List[int], List[List[int]]],
|
||||
skip_special_tokens: bool = True,
|
||||
**kwargs
|
||||
**kwargs,
|
||||
) -> str:
|
||||
"""
|
||||
Декодирует последовательность токенов в текст.
|
||||
|
||||
|
||||
Args:
|
||||
token_ids: ID токенов
|
||||
skip_special_tokens: Пропускать специальные токены
|
||||
|
||||
|
||||
Returns:
|
||||
str: Декодированный текст
|
||||
"""
|
||||
# Обрабатываем разные форматы входных данных
|
||||
if isinstance(token_ids, int):
|
||||
token_ids = [token_ids]
|
||||
elif isinstance(token_ids, list) and len(token_ids) > 0 and isinstance(token_ids[0], list):
|
||||
elif (
|
||||
isinstance(token_ids, list)
|
||||
and len(token_ids) > 0
|
||||
and isinstance(token_ids[0], list)
|
||||
):
|
||||
# Список списков - берем первый элемент
|
||||
token_ids = token_ids[0]
|
||||
|
||||
|
||||
# Фильтруем специальные токены если нужно
|
||||
if skip_special_tokens:
|
||||
special_ids = {self.pad_token_id, self.unk_token_id, self.bos_token_id, self.eos_token_id}
|
||||
special_ids = {
|
||||
self.pad_token_id,
|
||||
self.unk_token_id,
|
||||
self.bos_token_id,
|
||||
self.eos_token_id,
|
||||
}
|
||||
token_ids = [tid for tid in token_ids if tid not in special_ids]
|
||||
|
||||
|
||||
return self.llm_tokenizer.decode(token_ids)
|
||||
|
||||
|
||||
def tokenize(self, text: str, **kwargs) -> List[str]:
|
||||
"""
|
||||
Токенизирует текст в список строковых токенов.
|
||||
|
||||
|
||||
Args:
|
||||
text: Входной текст
|
||||
|
||||
|
||||
Returns:
|
||||
List[str]: Список токенов
|
||||
"""
|
||||
return self.llm_tokenizer.tokenize(text)
|
||||
|
||||
|
||||
def pad(
|
||||
self,
|
||||
encoded_inputs,
|
||||
@@ -202,7 +207,7 @@ class HFTokenizerAdapter:
|
||||
):
|
||||
"""
|
||||
Pad a list of encoded inputs.
|
||||
|
||||
|
||||
Args:
|
||||
encoded_inputs: List of encoded inputs
|
||||
padding: Padding strategy
|
||||
@@ -211,7 +216,7 @@ class HFTokenizerAdapter:
|
||||
return_attention_mask: Return attention mask
|
||||
return_tensors: Return tensors
|
||||
verbose: Verbose mode
|
||||
|
||||
|
||||
Returns:
|
||||
Padded inputs
|
||||
"""
|
||||
@@ -224,47 +229,62 @@ class HFTokenizerAdapter:
|
||||
# Обрабатываем разные типы данных
|
||||
if isinstance(input_ids, int):
|
||||
seq_len = 1
|
||||
elif hasattr(input_ids, 'shape'):
|
||||
seq_len = input_ids.shape[-1] if len(input_ids.shape) > 1 else len(input_ids)
|
||||
elif hasattr(input_ids, "shape"):
|
||||
seq_len = (
|
||||
input_ids.shape[-1]
|
||||
if len(input_ids.shape) > 1
|
||||
else len(input_ids)
|
||||
)
|
||||
else:
|
||||
seq_len = len(input_ids)
|
||||
max_len = max(max_len, seq_len)
|
||||
|
||||
|
||||
if max_length is not None:
|
||||
max_len = min(max_len, max_length)
|
||||
|
||||
|
||||
# Применяем padding
|
||||
for item in encoded_inputs:
|
||||
input_ids = item["input_ids"]
|
||||
|
||||
|
||||
# Получаем текущую длину
|
||||
if isinstance(input_ids, int):
|
||||
current_len = 1
|
||||
elif hasattr(input_ids, 'shape'):
|
||||
current_len = input_ids.shape[-1] if len(input_ids.shape) > 1 else len(input_ids)
|
||||
elif hasattr(input_ids, "shape"):
|
||||
current_len = (
|
||||
input_ids.shape[-1]
|
||||
if len(input_ids.shape) > 1
|
||||
else len(input_ids)
|
||||
)
|
||||
else:
|
||||
current_len = len(input_ids)
|
||||
|
||||
|
||||
if current_len < max_len:
|
||||
# Дополняем pad_token_id
|
||||
padding_length = max_len - current_len
|
||||
|
||||
|
||||
# Обрабатываем разные типы данных
|
||||
if isinstance(input_ids, int):
|
||||
item["input_ids"] = [input_ids] + [self.pad_token_id] * padding_length
|
||||
elif hasattr(input_ids, 'shape'):
|
||||
item["input_ids"] = [input_ids] + [
|
||||
self.pad_token_id
|
||||
] * padding_length
|
||||
elif hasattr(input_ids, "shape"):
|
||||
import torch
|
||||
padding_tensor = torch.full((padding_length,), self.pad_token_id, dtype=input_ids.dtype)
|
||||
|
||||
padding_tensor = torch.full(
|
||||
(padding_length,), self.pad_token_id, dtype=input_ids.dtype
|
||||
)
|
||||
item["input_ids"] = torch.cat([input_ids, padding_tensor])
|
||||
else:
|
||||
item["input_ids"] = input_ids + [self.pad_token_id] * padding_length
|
||||
|
||||
item["input_ids"] = (
|
||||
input_ids + [self.pad_token_id] * padding_length
|
||||
)
|
||||
|
||||
# Добавляем attention_mask если требуется
|
||||
if "attention_mask" in item:
|
||||
mask = item["attention_mask"]
|
||||
if isinstance(mask, int):
|
||||
item["attention_mask"] = [mask] + [0] * padding_length
|
||||
elif hasattr(mask, 'shape'):
|
||||
elif hasattr(mask, "shape"):
|
||||
padding_mask = torch.zeros(padding_length, dtype=mask.dtype)
|
||||
item["attention_mask"] = torch.cat([mask, padding_mask])
|
||||
else:
|
||||
@@ -272,44 +292,49 @@ class HFTokenizerAdapter:
|
||||
elif return_attention_mask:
|
||||
if isinstance(input_ids, int):
|
||||
item["attention_mask"] = [1] + [0] * padding_length
|
||||
elif hasattr(input_ids, 'shape'):
|
||||
elif hasattr(input_ids, "shape"):
|
||||
attention_mask = torch.ones(current_len, dtype=torch.long)
|
||||
padding_mask = torch.zeros(padding_length, dtype=torch.long)
|
||||
item["attention_mask"] = torch.cat([attention_mask, padding_mask])
|
||||
item["attention_mask"] = torch.cat(
|
||||
[attention_mask, padding_mask]
|
||||
)
|
||||
else:
|
||||
item["attention_mask"] = [1] * current_len + [0] * padding_length
|
||||
|
||||
item["attention_mask"] = [1] * current_len + [
|
||||
0
|
||||
] * padding_length
|
||||
|
||||
# Конвертируем в тензоры если требуется
|
||||
if return_tensors == "pt":
|
||||
import torch
|
||||
|
||||
for key in list(encoded_inputs[0].keys()):
|
||||
if isinstance(encoded_inputs[0][key], list):
|
||||
for i in range(len(encoded_inputs)):
|
||||
encoded_inputs[i][key] = torch.tensor(encoded_inputs[i][key])
|
||||
|
||||
|
||||
return encoded_inputs
|
||||
|
||||
|
||||
def get_vocab(self) -> Dict[str, int]:
|
||||
"""Возвращает словарь токенизатора."""
|
||||
return self._vocab
|
||||
|
||||
|
||||
def __len__(self) -> int:
|
||||
"""Возвращает размер словаря."""
|
||||
return self.vocab_size
|
||||
|
||||
|
||||
def save_pretrained(self, save_directory: str, **kwargs):
|
||||
"""
|
||||
Сохраняет токенизатор в формате HuggingFace.
|
||||
|
||||
|
||||
Args:
|
||||
save_directory: Директория для сохранения
|
||||
**kwargs: Дополнительные параметры
|
||||
"""
|
||||
import os
|
||||
|
||||
|
||||
# Создаем директорию если не существует
|
||||
os.makedirs(save_directory, exist_ok=True)
|
||||
|
||||
|
||||
# Сохраняем конфигурацию токенизатора
|
||||
tokenizer_config = {
|
||||
"tokenizer_class": self.__class__.__name__,
|
||||
@@ -324,77 +349,81 @@ class HFTokenizerAdapter:
|
||||
"bos_token_id": self.bos_token_id,
|
||||
"eos_token_id": self.eos_token_id,
|
||||
}
|
||||
|
||||
|
||||
config_path = os.path.join(save_directory, "tokenizer_config.json")
|
||||
with open(config_path, 'w', encoding='utf-8') as f:
|
||||
with open(config_path, "w", encoding="utf-8") as f:
|
||||
json.dump(tokenizer_config, f, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
# Сохраняем словарь
|
||||
vocab_path = os.path.join(save_directory, "vocab.json")
|
||||
with open(vocab_path, 'w', encoding='utf-8') as f:
|
||||
with open(vocab_path, "w", encoding="utf-8") as f:
|
||||
json.dump(self._vocab, f, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
print(f"✅ Токенизатор сохранен в {save_directory}")
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_pretrained(cls, pretrained_model_name_or_path: str, **kwargs):
|
||||
"""
|
||||
Загружает адаптированный токенизатор.
|
||||
|
||||
|
||||
Args:
|
||||
pretrained_model_name_or_path: Путь к сохраненному токенизатору
|
||||
**kwargs: Дополнительные параметры
|
||||
|
||||
|
||||
Returns:
|
||||
HFTokenizerAdapter: Загруженный адаптер
|
||||
"""
|
||||
import os
|
||||
|
||||
|
||||
# Проверяем, является ли путь директорией с файлами токенизатора
|
||||
if os.path.isdir(pretrained_model_name_or_path):
|
||||
# Загружаем из директории
|
||||
config_path = os.path.join(pretrained_model_name_or_path, "tokenizer_config.json")
|
||||
config_path = os.path.join(
|
||||
pretrained_model_name_or_path, "tokenizer_config.json"
|
||||
)
|
||||
vocab_path = os.path.join(pretrained_model_name_or_path, "vocab.json")
|
||||
|
||||
|
||||
if not os.path.exists(config_path) or not os.path.exists(vocab_path):
|
||||
raise FileNotFoundError(
|
||||
f"Файлы токенизатора не найдены в {pretrained_model_name_or_path}"
|
||||
)
|
||||
|
||||
|
||||
# Загружаем конфигурацию
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
with open(config_path, "r", encoding="utf-8") as f:
|
||||
config = json.load(f)
|
||||
|
||||
|
||||
# Определяем тип токенизатора llm
|
||||
llm_tokenizer_type = config.get("llm_tokenizer_type", "BPETokenizer")
|
||||
|
||||
|
||||
if llm_tokenizer_type == "BPETokenizer":
|
||||
# Создаем BPETokenizer и загружаем словарь
|
||||
llm_tokenizer = BPETokenizer()
|
||||
|
||||
|
||||
# Загружаем словарь
|
||||
with open(vocab_path, 'r', encoding='utf-8') as f:
|
||||
with open(vocab_path, "r", encoding="utf-8") as f:
|
||||
vocab = json.load(f)
|
||||
|
||||
|
||||
llm_tokenizer.vocab = vocab
|
||||
llm_tokenizer.inverse_vocab = {v: k for k, v in vocab.items()}
|
||||
llm_tokenizer.vocab_size = len(vocab)
|
||||
|
||||
|
||||
# Устанавливаем специальные токены
|
||||
llm_tokenizer.pad_token = config.get("pad_token", "<pad>")
|
||||
llm_tokenizer.unk_token = config.get("unk_token", "<unk>")
|
||||
llm_tokenizer.bos_token = config.get("bos_token", "<bos>")
|
||||
llm_tokenizer.eos_token = config.get("eos_token", "<eos>")
|
||||
|
||||
|
||||
llm_tokenizer.pad_token_id = config.get("pad_token_id", 0)
|
||||
llm_tokenizer.unk_token_id = config.get("unk_token_id", 1)
|
||||
llm_tokenizer.bos_token_id = config.get("bos_token_id", 2)
|
||||
llm_tokenizer.eos_token_id = config.get("eos_token_id", 3)
|
||||
|
||||
|
||||
return cls(llm_tokenizer, **kwargs)
|
||||
else:
|
||||
raise ValueError(f"Неподдерживаемый тип токенизатора: {llm_tokenizer_type}")
|
||||
|
||||
raise ValueError(
|
||||
f"Неподдерживаемый тип токенизатора: {llm_tokenizer_type}"
|
||||
)
|
||||
|
||||
else:
|
||||
# Пытаемся загрузить как файл llm токенизатора
|
||||
try:
|
||||
@@ -409,10 +438,10 @@ class HFTokenizerAdapter:
|
||||
def create_hf_tokenizer(llm_tokenizer: BaseTokenizer) -> HFTokenizerAdapter:
|
||||
"""
|
||||
Создает адаптер HuggingFace для кастомного токенизатора.
|
||||
|
||||
|
||||
Args:
|
||||
llm_tokenizer: Токенизатор из библиотеки llm
|
||||
|
||||
|
||||
Returns:
|
||||
HFTokenizerAdapter: Адаптированный токенизатор
|
||||
"""
|
||||
@@ -422,7 +451,7 @@ def create_hf_tokenizer(llm_tokenizer: BaseTokenizer) -> HFTokenizerAdapter:
|
||||
def convert_to_hf_format(llm_tokenizer: BaseTokenizer, save_directory: str):
|
||||
"""
|
||||
Конвертирует кастомный токенизатор в формат HuggingFace.
|
||||
|
||||
|
||||
Args:
|
||||
llm_tokenizer: Токенизатор из llm
|
||||
save_directory: Директория для сохранения
|
||||
|
||||
@@ -14,55 +14,57 @@ 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"
|
||||
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__):
|
||||
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,
|
||||
@@ -70,11 +72,11 @@ class HFUtils:
|
||||
repo_name: str,
|
||||
organization: Optional[str] = None,
|
||||
private: bool = False,
|
||||
**kwargs
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
Загружает модель в HuggingFace Hub.
|
||||
|
||||
|
||||
Args:
|
||||
model: Адаптированная модель
|
||||
tokenizer: Токенизатор
|
||||
@@ -85,23 +87,23 @@ class HFUtils:
|
||||
"""
|
||||
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,
|
||||
@@ -110,46 +112,43 @@ class HFUtils:
|
||||
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"
|
||||
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:
|
||||
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,
|
||||
@@ -159,63 +158,56 @@ class HFUtils:
|
||||
"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)
|
||||
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"
|
||||
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'])
|
||||
|
||||
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'
|
||||
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
|
||||
hf_logits.flatten(), llm_logits.flatten(), dim=0
|
||||
)
|
||||
|
||||
|
||||
return {
|
||||
"kl_divergence": kl_divergence.item(),
|
||||
"cosine_similarity": cosine_similarity.item(),
|
||||
@@ -228,58 +220,52 @@ 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
|
||||
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
|
||||
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 {
|
||||
@@ -290,36 +276,27 @@ class TokenizerWrapper:
|
||||
}
|
||||
|
||||
|
||||
def create_hf_pipeline(
|
||||
llm_model,
|
||||
tokenizer=None,
|
||||
device: str = "auto",
|
||||
**kwargs
|
||||
):
|
||||
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
|
||||
"text-generation", model=hf_model, tokenizer=tokenizer, device=device, **kwargs
|
||||
)
|
||||
|
||||
|
||||
return pipe
|
||||
|
||||
Reference in New Issue
Block a user