Проблема: Смешались в кучу адаптеры, люди
Вы тренируете модель на медицинских текстах. Потом на юридических. Потом на технической документации. Каждый раз используете LoRA - легковесные адаптеры, которые дешево и сердито подстраивают большую модель под новую задачу. И всё бы ничего, но когда вы пытаетесь заставить модель работать со всеми тремя доменами одновременно, получается каша. Медицинские термины проскакивают в юридических документах, формулы лезут в диагнозы. Это катастрофическое забывание в чистом виде, только теперь оно мультидоменное.
Temporal LoRA: не смешивать, а переключать
Идея Temporal LoRA проста до гениальности. Вместо того чтобы пытаться усреднить все адаптеры или заставить модель работать со всеми сразу, мы ставим интеллектуальный переключатель - gating network. Эта маленькая нейросеть анализирует входной текст и решает: какой адаптер сейчас нужен? Медицинский, юридический или технический. И активирует только его.
Архитектура выглядит так:
- Базовая модель (например, GPT-2)
- Несколько LoRA-адаптеров, каждый обучен на своем домене
- Роутер (gating network) - легкая сеть, которая принимает решение
- Механизм внимания, который взвешивает выходы адаптеров
1 Как обучается роутер
Самое интересное - обучение. Роутер учится не на самих доменах, а на мета-признаках. Ему показывают: вот медицинский текст, вот юридический. Он должен научиться их различать по стилю, терминологии, структуре. В PoC на GPT-2 использовали простой подход: бинарная классификация с учителем. На вход - embedding текста, на выход - вероятность принадлежности к домену.
class TemporalLoRARouter(nn.Module):
def __init__(self, hidden_size, num_adapters):
super().__init__()
self.linear1 = nn.Linear(hidden_size, 256)
self.linear2 = nn.Linear(256, num_adapters)
self.softmax = nn.Softmax(dim=-1)
def forward(self, input_embeddings):
# input_embeddings: [batch_size, seq_len, hidden_size]
pooled = input_embeddings.mean(dim=1) # [batch_size, hidden_size]
x = F.relu(self.linear1(pooled))
logits = self.linear2(x) # [batch_size, num_adapters]
weights = self.softmax(logits) # [batch_size, num_adapters]
return weights
100% точность переключения: миф или реальность?
В экспериментах на GPT-2 с тремя доменами (медицина, право, техника) роутер достиг 100% точности на тестовой выборке. Звучит невероятно? Объяснение простое: домены были хорошо разделены. Медицинские тексты содержали специфические термины ("анамнез", "диагноз"), юридические - шаблонные фразы ("настоящим договором", "стороны согласовали"). Роутер научился ловить эти маркеры.
Но не обольщайтесь. 100% - это на чистом, стерильном датасете. В реальности, когда тексты смешанные или домены похожи (скажем, финансы и юриспруденция), точность упадет до 80-90%. И это нормально.
Сравнение: Temporal LoRA против альтернатив
| Метод | Плюсы | Минусы | Когда использовать |
|---|---|---|---|
| Классическая LoRA | Простота, мало параметров | Только одна задача, смешение адаптеров убивает качество | Когда нужна быстрая адаптация под один домен |
| Смешение адаптеров (AdapterFusion) | Можно комбинировать несколько доменов | Качество ниже, модель путается | Когда домены слабо различимы |
| Mixture of Experts (MoE) | Мощно, масштабируется | Сложно обучать, требует тонны ресурсов | Для гигантских моделей вроде GPT-4 |
| Temporal LoRA | Чистое переключение контекстов, нет забывания | Нужен роутер, домены должны быть различимы | Когда есть четкие домены и нужно сохранить качество каждого |
Главное отличие от MoE - легкость. Temporal LoRA не пытается быть универсальным решением для всех задач. Это специализированный инструмент для конкретной проблемы: разделения доменов. Если вам нужно что-то похожее, но для балансировки нагрузки между экспертами, посмотрите на Tuneable Attention.
Код: как это работает на практике
Вот упрощенная версия forward pass в Temporal LoRA. Полный код эксперимента доступен в репозитории авторов.
def temporal_lora_forward(model, input_ids, attention_mask):
"""Упрощенный forward pass с Temporal LoRA"""
# Получаем эмбеддинги входного текста
embeddings = model.get_input_embeddings()(input_ids)
# Роутер решает, какие адаптеры активировать
adapter_weights = router(embeddings) # [batch_size, num_adapters]
# Базовый forward pass модели
base_output = model.base_model(input_ids, attention_mask=attention_mask)
# Применяем каждый адаптер и взвешиваем результаты
adapter_outputs = []
for i, adapter in enumerate(adapters):
adapter_output = adapter(base_output.last_hidden_state)
weighted_output = adapter_output * adapter_weights[:, i].unsqueeze(-1).unsqueeze(-1)
adapter_outputs.append(weighted_output)
# Суммируем взвешенные выходы адаптеров
combined_output = torch.stack(adapter_outputs).sum(dim=0)
# Возвращаем финальный результат
return model.lm_head(combined_output)
Обучение происходит в два этапа: сначала тренируете адаптеры на отдельных доменах (обычный LoRA), потом тренируете роутер на размеченных данных (какой текст к какому домену относится).
Кому нужен Temporal LoRA?
Эта технология не для всех. Она идеально подойдет:
- Разработчикам корпоративных чат-ботов, которые должны отвечать на вопросы из разных отделов (HR, техподдержка, продажи) без смешения контекстов
- Исследователям, работающим с многодоменными датасетами, где важно сохранить чистоту стилей
- Компаниям, которые хотят использовать одну модель для всех задач, но боятся дрейфа интерпретации
- Энтузиастам Stability-First AI, для которых предсказуемость важнее максимального качества
Если же вам нужно просто быстро дообучить модель под свою задачу и вы не планируете добавлять другие домены - берите классическую LoRA. Не усложняйте.
Что дальше? Будущее динамических адаптеров
Temporal LoRA - только начало. Следующий шаг - иерархические роутеры, которые умеют определять не только домен, но и поддомены. Например, внутри медицинского текста различать хирургию и терапию. Или внутри юридического - договоры и иски.
Другое направление - soft switching, когда роутер может активировать несколько адаптеров одновременно с разными весами. Это полезно для смешанных текстов (технико-юридические документы).
Самое интересное применение я вижу в связке с DPO. Представьте: модель учится не только переключаться между доменами, но и выбирать оптимальный стиль ответа внутри каждого домена. Юридический адаптер может иметь под-адаптеры для формального и простого языка.
Экспериментируйте. Но помните главное правило Stability-First AI: если что-то работает стабильно на 90% - не гонитесь за 95%. Чаще всего эти 5% стоят вдвое больше ресурсов и втрое больше головной боли.