# Lily: Adaptación de bajo rango interconectada entre capas

[Lily](https://huggingface.co/papers/2407.09946) es una técnica de ajuste fino eficiente en parámetros que introduce el intercambio de pesos entre capas para las matrices de adaptador. En lugar de aprender un par AB independiente por capa, como en LoRA, Lily utiliza **adaptadores A compartidos localmente** (cada A se comparte entre un bloque de `stride_A` capas consecutivas) y **expertos B compartidos globalmente** (un pequeño conjunto de `num_B` adaptadores B se comparte entre todas las capas). En cada pasada hacia adelante, un enrutador ligero dependiente de los datos calcula una combinación ponderada con softmax de los expertos B para producir el B efectivo para esa capa y entrada.

Este intercambio puede reducir el número total de matrices de adaptador de `2N` (LoRA estándar) a `N / stride_A + num_B`, liberando el presupuesto de parámetros para usar un **rango `r` mucho mayor** —típicamente `2×`–`4×` lo que se usaría en LoRA. Un rango más alto y una mejor interconectividad aumentan el rango efectivo de la actualización de pesos `ΔW = A × combined_B`, lo que conduce a un mejor rendimiento de adaptación.

Dado que la combinación B es **dependiente de los datos** (los pesos del router dependen de las activaciones de entrada en tiempo de ejecución), `merge` y `unmerge` no están **soportados**. Si se requiere la fusión de pesos para su despliegue, considere otros métodos, como LoRA.

Lily tiene actualmente las siguientes restricciones adicionales:
- Solo se admiten capas `nn.Linear`.
- No se admiten capas cuantizadas.

Si estas restricciones no se ajustan a su caso de uso, considere otros métodos.

El resumen del artículo es:

> La adaptación de bajo rango (LoRA) es un método de ajuste fino eficiente en parámetros (PEFT) ampliamente utilizado que aprende actualizaciones de pesos ΔW = AB para los pesos preentrenados W a través de adaptadores de bajo rango A y B. Si bien LoRA garantiza la eficiencia de hardware, sus actualizaciones de pesos de bajo rango limitan el rendimiento de la adaptación. En este artículo, proponemos la adaptación interconectada de bajo rango entre capas (Lily), un nuevo método PEFT que introduce un marco interconectado con expertos A compartidos localmente y expertos B compartidos globalmente. Esta estructura elimina las pares AB redundantes por capa, lo que permite un ΔW de rango superior con igual o menor número de parámetros. Para mejorar la expresividad, utilizamos enrutadores dependientes de los datos para determinar las interconexiones A-B, evitando que los expertos B converjan al mismo comportamiento y mejorando el poder representativo entre dominios. Los experimentos en diferentes modalidades, arquitecturas y tamaños de modelo demuestran el rendimiento y la eficiencia superiores de Lily.

## Resumen de las pruebas de rendimiento

<iframe src="https://peft-internal-testing-peft-method-comparison-embed.hf.space/?highlight[type]=LILY" frameborder="0" width="850" height="1000"
>

# API

## LilyConfig[[peft.LilyConfig]]

#### peft.LilyConfig[[peft.LilyConfig]]

```python
peft.LilyConfig(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, r: int = 32, stride_A: int = 1, num_B: int = 2, scaling: float = 1.0, target_modules: Optional[Union[list[str], str]] = None, exclude_modules: Optional[Union[list[str], str]] = None, modules_to_save: Optional[list[str]] = None, layers_to_transform: Optional[Union[list[int], int]] = None, layers_pattern: Optional[Union[list[str], str]] = None, init_weights: bool = True)
```

[Fuente](https://github.com/huggingface/peft/blob/main/src/peft/tuners/lily/config.py#L24)

**Parámetros:**

r (`int`) : Rango de Lily. Determina la dimensión oculta interna de cada adaptador y el rango de la actualización de pesos `A @ B`. En Lily, dado que el número de adaptadores suele ser menor que en LoRA, cada adaptador necesita tener más capacidad, por lo que se recomienda usar un `r` mayor que en LoRA, típicamente `2x`, `3x` o `4x` el rango de LoRA que se usaría normalmente. El número total de parámetros entrenables escala con `r * (total_layers / stride_A + num_B)`, por lo que aumentar `r` manteniendo `stride_A` grande y `num_B` pequeño es el compromiso recomendado.

stride_A (`int`) : El número de capas consecutivas que comparten un adaptador A. Por ejemplo, si `stride_A=4`, cada 4 capas adyacentes comparten el mismo adaptador A, lo que resulta en `total_layers / stride_A` adaptadores A distintos en total. El adaptador A comprime la entrada en una representación de rango bajo de tamaño `r`. `stride_A` no debe ser menor que 1. Valores sugeridos: `2`, `3` o `4` (es decir, compartir cada 2, 3 o 4 capas). Mantener `stride_A` grande (menos adaptadores A distintos) y aumentar `r` en su lugar conduce a un mejor rendimiento que el compromiso opuesto (pequeño `stride_A`, pequeño `r`). Establecer `stride_A=1` significa que cada capa tiene su propio adaptador A. NOTA: el compartimiento de A ocurre dentro de cada objetivo (capas con el mismo sufijo de objetivo). Por ejemplo, si tus target_modules son `['q_proj', 'v_proj']` y estableces `stride_A=2`, entonces cada 2 capas adyacentes de q_proj compartirán un adaptador A, y cada 2 capas adyacentes de v_proj compartirán otro adaptador A, pero las capas de q_proj y v_proj no compartirán adaptadores A entre sí, ya que tienen sufijos diferentes.

num_B (`int`) : El número de adaptadores B compartidos. A diferencia de los adaptadores A (que se agrupan por capa), todos los adaptadores B se comparten globalmente entre todas las capas. En cada pasada hacia adelante, un router calcula una combinación ponderada de todos los adaptadores B `num_B` (usando pesos normalizados con softmax) para producir un único adaptador B combinado, que luego proyecta la representación de bajo rango de vuelta a la dimensión original. Se recomienda establecer `num_B` en un orden similar al de `total_layers / stride_A`. Valores sugeridos: `total_layers / 2`, `total_layers / 3` o `total_layers / 4`. Al igual que con `stride_A`, se prefiere un `num_B` más pequeño con un `r` más grande que un `num_B` más grande con un `r` más pequeño. NOTA: para entrenar el router, se necesitan al menos 2 adaptadores B (es decir, `num_B >= 2`), ya que el router aprende a calcular una combinación ponderada de los adaptadores B. NOTA: el compartimiento de B ocurre dentro de cada objetivo (capas con el mismo sufijo de objetivo). Por ejemplo, si tus target_modules son `['q_proj', 'v_proj']` y estableces `num_B=4`, habrá 4 adaptadores B compartidos entre todas las capas q_proj y otros 4 adaptadores B compartidos entre todas las capas v_proj, pero las capas q_proj y v_proj no compartirán adaptadores B entre sí porque tienen sufijos diferentes.

target_modules (`Union[List[str], str]`, *opcional*) : Los nombres de los módulos a los que se aplicará Lily. Puede ser una lista de cadenas de nombres de módulos (p. ej. `['q_proj', 'v_proj']`) o un patrón de expresión regular (p. ej. `'.*decoder.*(SelfAttention|EncDecAttention).*(q|v)$'`). Si no se especifica, Lily se aplicará a todas las capas lineales compatibles.

scaling (`float`) : Un multiplicador escalar aplicado a la salida combinada del adaptador (`scaling * A @ combined_B`) antes de sumarla al paso hacia adelante del peso congelado. A diferencia de LoRA, Lily no utiliza una formulación de `alpha / r`; en su lugar, `scaling` es un multiplicador directo. Este diseño facilita realizar barridos de valores en una escala logarítmica (p. ej. `0.01`, `0.1`, `1.0`, `10.0`). El valor óptimo depende de la tarea y debe tratarse como un hiperparámetro. Recomendamos comenzar con `1.0`.

modules_to_save (`List[str]`, *opcional*) : Lista de módulos, aparte de las capas de Lily, que se configurarán como entrenables y se guardarán en el checkpoint final. Por ejemplo, en tareas de Clasificación de Secuencias o Clasificación de Tokens, la capa final `classifier/score` se inicializa aleatoriamente y, por lo tanto, debe ser entrenable y guardada.

exclude_modules (`Union[List[str], str]`, *opcional*) : Los nombres de los módulos a los que no se aplicará el adaptador. Al pasar una cadena de caracteres, se realizará una coincidencia con una expresión regular. Al pasar una lista de cadenas de caracteres, se realizará una coincidencia exacta o se comprobará si el nombre del módulo termina con alguna de las cadenas de caracteres pasadas.

layers_to_transform (`Union[list[int], int]`, *opcional*) : Los índices de las capas a transformar. Si se especifica este argumento, PEFT transformará solo los índices de las capas especificados dentro de esta lista. Si se pasa un solo entero, PEFT transformará solo la capa en ese índice.

layers_pattern (`Optional[Union[List[str], str]]`, *opcional*) : El nombre del patrón de capa, utilizado solo si `layers_to_transform` es diferente de None y si el patrón de capa no está en el patrón de capas común. Esto debe apuntar al `nn.ModuleList` del modelo, que a menudo se llama `'layers'` o `'h'`.

init_weights (`bool`) : Si se deben inicializar los pesos del adaptador Lily usando el esquema de inicialización predeterminado: las matrices A se inicializan con Kaiming uniform y las matrices B se inicializan a cero, lo que garantiza que la salida del adaptador sea cero al inicio del entrenamiento y no perturbe el modelo preentrenado. Se recomienda encarecidamente mantenerlo como `True` a menos que se tenga una razón específica para cambiarlo.

Esta es la clase de configuración para almacenar la configuración de un [LilyModel](/docs/peft/main/es/package_reference/lily#peft.LilyModel).

## LilyModel[[peft.LilyModel]]

#### peft.LilyModel[[peft.LilyModel]]

```python
peft.LilyModel(model, peft_config: Union[PeftConfig, dict[str, PeftConfig]], adapter_name: str, low_cpu_mem_usage: bool = False, state_dict: Optional[dict[str, torch.Tensor]] = None)
```

[Fuente](https://github.com/huggingface/peft/blob/main/src/peft/tuners/lily/model.py#L30)

**Parámetros:**

model (`torch.nn.Module`) : El modelo que se va a adaptar.

config ([LilyConfig](/docs/peft/main/es/package_reference/lily#peft.LilyConfig)) : La configuración del modelo Lily.

adapter_name (`str`) : El nombre del adaptador, por defecto es `"default"`.

**Devuelve:** `torch.nn.Module`

El modelo PEFT de Lily.

Crea un modelo Low-Rank Interconnected Adaptation Across Layers (Lily) a partir de un modelo de transformers preentrenado.

El método se describe en detalle en https://arxiv.org/abs/2407.09946.

**Atributos**:
- **model** ([PreTrainedModel](https://huggingface.co/docs/transformers/main/en/main_classes/model#transformers.PreTrainedModel)) -- El modelo que se va a adaptar.
- **peft_config** ([LilyConfig](/docs/peft/main/es/package_reference/lily#peft.LilyConfig)): La configuración del modelo Lily.

