# Картриджи

Cartridges — это метод обучения по промптам, который хранит сжатое представление длинного контекста в виде параметризованного префикса KV-кэша. Основная идея взята из статьи [Cartridges: Lightweight and general-purpose long context representations via self-study](https://huggingface.co/papers/2506.06266).

Для высокоуровневого обзора и мотивации см. блог-пост [Cartridges: Storing long contexts in tiny caches with self-study](https://hazyresearch.stanford.edu/blog/2025-06-08-cartridges).

## Чем Cartridges отличаются от Prefix Tuning

Оба метода, Prefix Tuning и Cartridges, работают за счёт внедрения `past_key_values` (префиксного KV-кэша) в базовую модель.

- Prefix Tuning обучает эмбеддинги виртуальных токенов (и, при необходимости, проекцию MLP) и формирует KV-префикс.
- Cartridges напрямую обучают сам KV-префикс (векторы ключей/значений для каждого слоя для `p` виртуальных токенов) и предназначены для инициализации из реального KV-префикса prefill (например, из первых `p` токенов корпуса/системного промпта).

В статье также рекомендуется замораживать первый токен в качестве attention sink для обеспечения стабильности (по умолчанию `num_frozen_tokens=1`).

## Usage (инференс)

Загрузите обученный адаптер CARTRIDGE и запустите генерацию:

```py
from transformers import AutoModelForCausalLM, AutoTokenizer

from peft import PeftModel

model_id = "Qwen/Qwen2.5-0.5B-Instruct"
adapter_path = "path/to/cartridge_adapter"

base = AutoModelForCausalLM.from_pretrained(model_id)
model = PeftModel.from_pretrained(base, adapter_path)

tok = AutoTokenizer.from_pretrained(model_id)
if tok.pad_token is None:
    tok.pad_token = tok.eos_token

out = model.generate(**tok("Question about the corpus:", return_tensors="pt"), max_new_tokens=64)
print(tok.decode(out[0], skip_special_tokens=True))
```

Если вам нужно создать и инициализировать картридж перед обучением, см. варианты инициализации ниже.

## Варианты инициализации

В статье обсуждается несколько практических стратегий инициализации:

- Случайный KV (по умолчанию): создайте `CartridgeConfig` и начните обучение. Это инициализирует KV-префикс случайным образом.
- KV из первых токенов промпта/корпуса: используйте `initialize_kv_prefix_from_text(model, tokenizer, text=...)`. Эта функция выполняет prefill для `text` и копирует полученный KV-кэш для первых `num_virtual_tokens` в адаптер.
- KV из существующего кэша: используйте `initialize_kv_prefix_from_past_key_values(model, past_key_values=...)`, если у вас уже есть объект `past_key_values` из префилла базовой модели.

## Обучение

Статья Cartridges предлагает цель дистилляции SELF-STUDY (замороженная базовая модель предоставляет teacher logits; адаптер CARTRIDGE обучается так, чтобы модель-студент совпадала с распределением следующего токена учителя на целевом сегменте). PEFT выносит логику обучения за пределы основной библиотеки; см. `https://github.com/huggingface/peft/tree/main/examples/cartridge_self_study` для эталонного рабочего процесса. Примеры скриптов используют замороженную базовую модель в качестве учителя и адаптированную модель в качестве студента, поэтому обе разделяют один и тот же базовый чекпоинт.

## Состав

Чтобы объединить независимо обученные картриджи в единый адаптер, используйте `compose_cartridge_adapters(...)`.

# API

## CartridgeConfig[[peft.CartridgeConfig]]

#### peft.CartridgeConfig[[peft.CartridgeConfig]]

```python
peft.CartridgeConfig(task_type: Optional[Union[str, TaskType]] = None, peft_type: Optional[Union[str, PeftType]] = None, auto_mapping: Optional[dict] = None, peft_version: Optional[str] = None, base_model_name_or_path: Optional[str] = None, revision: Optional[str] = None, inference_mode: bool = False, num_virtual_tokens: int = None, token_dim: int = None, num_transformer_submodules: Optional[int] = None, num_attention_heads: Optional[int] = None, num_layers: Optional[int] = None, modules_to_save: Optional[list[str]] = None, num_frozen_tokens: int = 1)
```

[Источник](https://github.com/huggingface/peft/blob/main/src/peft/tuners/cartridge/config.py#L22)

**Параметры:**

num_frozen_tokens (`int`, по умолчанию 1) : Количество токенов *prefix* в начале картриджа, которые следует заморозить (без градиентов). В статье о Cartridges рекомендуется замораживать первый токен в качестве attention sink для стабильности (установите это значение в `1`), поскольку многие LLM используют начальные токены в качестве attention sink, и их изменение может навредить обучению.

Конфигурация для CARTRIDGE, префиксного адаптера с параметризованной KV-кэшем.

Это похоже на prefix-tuning по способу обслуживания (в виде `past_key_values`), но кэш KV хранится непосредственно как обучаемые параметры, а не обучается через проекцию MLP.

Инициализация: В статье Cartridges обсуждается несколько вариантов инициализации. В PEFT инициализация является *отдельным* шагом от создания конфигурации адаптера:

- **Случайная инициализация KV (вариант 2 из статьи)**: создайте адаптер с помощью `get_peft_model(...)`. Параметры кодировщика промптов CARTRIDGE инициализируются случайно PyTorch.

- **KV, полученные из первых токенов промпта/корпуса (вариант 3 из статьи)**: выполните prefill без градиентов на *базовой модели* и скопируйте первые `num_virtual_tokens` токенов кэшированного KV в адаптер. PEFT предоставляет утилиты для этого (импортируемые из `peft` или из `peft.tuners.cartridge.utils`):

  - `initialize_kv_prefix_from_text(model, tokenizer, text=...)`
  - `initialize_kv_prefix_from_past_key_values(model, past_key_values=...)`

  Если у вас уже есть тензор с выровненным KV-префиксом, вы можете загрузить его напрямую с помощью метода `load_prompt_embeddings(...)` кодировщика промпта.

## CartridgeEncoder[[peft.CartridgeEncoder]]

#### peft.CartridgeEncoder[[peft.CartridgeEncoder]]

```python
peft.CartridgeEncoder(config)
```

[Источник](https://github.com/huggingface/peft/blob/main/src/peft/tuners/cartridge/model.py#L22)

Параметризованный префиксный кэш KV.

Параметры хранятся в том же плоском формате, что и вывод `PrefixEncoder`: `[num_virtual_tokens, num_layers
* 2 * token_dim]`, where `token_dim` is per-head hidden size times number of heads (after any GQA adjustment
выполняется `_prepare_prompt_learning_config`).

Если `num_frozen_tokens > 0`, первые `num_frozen_tokens` виртуальные токены сохраняются как необучаемый параметр, а остальные токены обучаемы.

#### load_prompt_embeddings[[peft.CartridgeEncoder.load_prompt_embeddings]]

```python
load_prompt_embeddings(prompt_embeddings: torch.Tensor)
```

[Источник](https://github.com/huggingface/peft/blob/main/src/peft/tuners/cartridge/model.py#L91)

Загрузите сглаженные эмбеддинги промпта, сохранённые PEFT (`prompt_embeddings`).

PEFT сохраняет адаптеры prompt-learning в виде единого тензора `prompt_embeddings`. Для CARTRIDGE мы разбиваем этот тензор на замороженный и обучаемый сегменты в соответствии с `self.num_frozen_tokens`.

## initialize_kv_prefix_from_past_key_values[[peft.initialize_kv_prefix_from_past_key_values]]

#### peft.initialize_kv_prefix_from_past_key_values[[peft.initialize_kv_prefix_from_past_key_values]]

```python
peft.initialize_kv_prefix_from_past_key_values(model, adapter_name: Optional[str] = None, past_key_values: Any, num_virtual_tokens: Optional[int] = None)
```

[Источник](https://github.com/huggingface/peft/blob/main/src/peft/tuners/cartridge/utils.py#L100)

Инициализируйте адаптер обучения по префиксу KV из существующего кэшированного префикса (`past_key_values`).

Возвращает тензор эмбеддингов промпта, загруженный в адаптер.

## prompt_embeddings_from_past_key_values[[peft.prompt_embeddings_from_past_key_values]]

#### peft.prompt_embeddings_from_past_key_values[[peft.prompt_embeddings_from_past_key_values]]

```python
peft.prompt_embeddings_from_past_key_values(past_key_values: Any, num_virtual_tokens: int)
```

[Источник](https://github.com/huggingface/peft/blob/main/src/peft/tuners/cartridge/utils.py#L48)

Преобразование (устаревшего) кэша `past_key_values` в тензор сглаженных эмбеддингов промпта, сохраняемый PEFT.

Вывод соответствует макету, ожидаемому `PeftModel.get_prompt()` для обучения промптов в префиксном стиле: форма `[num_virtual_tokens, num_layers * 2 * token_dim]`.

