# Personalizando componentes do modelo

Outra forma de personalizar um modelo é modificar seus componentes, em vez de escrever um novo modelo por completo, permitindo que você adapte o modelo ao seu caso de uso específico. Por exemplo, você pode adicionar novas camadas ou otimizar o mecanismo de atenção de uma arquitetura. As personalizações são aplicadas diretamente a um modelo do Transformers, para que você possa continuar usando recursos como [Trainer](/docs/transformers/main/pt/main_classes/trainer#transformers.Trainer), [PreTrainedModel](/docs/transformers/main/pt/main_classes/model#transformers.PreTrainedModel) e a biblioteca [PEFT](https://huggingface.co/docs/peft/en/index).

Este guia mostrará como personalizar o mecanismo de atenção de um modelo para aplicar [Low-Rank Adaptation (LoRA)](https://huggingface.co/docs/peft/conceptual_guides/adapter#low-rank-adaptation-lora) a ele.

> [!TIP]
> O utilitário [clear_import_cache](https://github.com/huggingface/transformers/blob/9985d06add07a4cc691dc54a7e34f54205c04d40/src/transformers/utils/import_utils.py#L2286) é muito útil quando você está modificando e desenvolvendo iterativamente o código de um modelo. Ele remove todos os módulos do Transformers em cache e permite que o Python recarregue o código modificado sem a necessidade de reiniciar constantemente o ambiente.
>
> ```py
> from transformers import AutoModel
> from transformers.utils.import_utils import clear_import_cache
>
> model = AutoModel.from_pretrained("bert-base-uncased")
> # modifications to model code
> # clear cache to reload modified code
> clear_import_cache()
> # re-import to use updated code
> model = AutoModel.from_pretrained("bert-base-uncased")
> ```

## Classe de atenção

[Segment Anything](./model_doc/sam) é um modelo de segmentação de imagem e combina a projeção query-key-value (`qkv`) em seus mecanismos de atenção. Para reduzir o número de parâmetros treináveis e a sobrecarga computacional, você pode aplicar LoRA à projeção `qkv`. Isso requer dividir a projeção `qkv` para que você possa direcionar separadamente o `q` e o `v` com LoRA.

1. Crie uma classe de atenção personalizada, `SamVisionAttentionSplit`, herdando da classe original `SamVisionAttention`. No `__init__`, remova o `qkv` combinado e crie uma camada linear separada para `q`, `k` e `v`.

```py
import torch
import torch.nn as nn
from transformers.models.sam.modeling_sam import SamVisionAttention

class SamVisionAttentionSplit(SamVisionAttention, nn.Module):
    def __init__(self, config, window_size):
        super().__init__(config, window_size)
        # remove combined qkv
        del self.qkv
        # separate q, k, v projections
        self.q = nn.Linear(config.hidden_size, config.hidden_size, bias=config.qkv_bias)
        self.k = nn.Linear(config.hidden_size, config.hidden_size, bias=config.qkv_bias)
        self.v = nn.Linear(config.hidden_size, config.hidden_size, bias=config.qkv_bias)
        self._register_load_state_dict_pre_hook(self.split_q_k_v_load_hook)
```

2. A função `split_q_k_v_load_hook` divide os pesos pré-treinados `qkv` em pesos separados `q`, `k` e `v` ao carregar o modelo, garantindo compatibilidade com qualquer modelo pré-treinado.

```py
    def split_q_k_v_load_hook(self, state_dict, prefix, *args):
        keys_to_delete = []
        for key in list(state_dict.keys()):
            if "qkv." in key:
                # split q, k, v from the combined projection
                q, k, v = state_dict[key].chunk(3, dim=0)
                # replace with individual q, k, v projections
                state_dict[key.replace("qkv.", "q.")] = q
                state_dict[key.replace("qkv.", "k.")] = k
                state_dict[key.replace("qkv.", "v.")] = v
                # mark the old qkv key for deletion
                keys_to_delete.append(key)
        
        # remove old qkv keys
        for key in keys_to_delete:
            del state_dict[key]
```

3. Na passagem `forward`, `q`, `k` e `v` são calculados separadamente, enquanto o restante do mecanismo de atenção permanece o mesmo.

```py
    def forward(self, hidden_states: torch.Tensor, output_attentions=False) -> torch.Tensor:
        batch_size, height, width, _ = hidden_states.shape
        qkv_shapes = (batch_size *  self.num_attention_heads,  height * width, -1)
        query = self.q(hidden_states).reshape((batch_size,  height * width,self.num_attention_heads, -1)).permute(0,2,1,3).reshape(qkv_shapes)
        key = self.k(hidden_states).reshape((batch_size,  height * width,self.num_attention_heads, -1)).permute(0,2,1,3).reshape(qkv_shapes)
        value = self.v(hidden_states).reshape((batch_size,  height * width,self.num_attention_heads, -1)).permute(0,2,1,3).reshape(qkv_shapes)

        attn_weights = (query * self.scale) @ key.transpose(-2, -1)

        attn_weights = torch.nn.functional.softmax(attn_weights, dtype=torch.float32, dim=-1).to(query.dtype)
        attn_probs = nn.functional.dropout(attn_weights, p=self.dropout, training=self.training)
        attn_output = (attn_probs @ value).reshape(batch_size, self.num_attention_heads, height, width, -1)
        attn_output = attn_output.permute(0, 2, 3, 1, 4).reshape(batch_size, height, width, -1)
        attn_output = self.proj(attn_output)

        if output_attentions:
            outputs = (attn_output, attn_weights)
        else:
            outputs = (attn_output, None)
        return outputs
```

Atribua a classe personalizada `SamVisionAttentionSplit` ao módulo `SamVisionAttention` dos modelos originais para substituí-lo. Todas as instâncias de `SamVisionAttention` no modelo são substituídas pela versão de atenção dividida.

Substitua a classe *antes* de chamar [from_pretrained()](/docs/transformers/main/pt/main_classes/model#transformers.PreTrainedModel.from_pretrained). O hook de carregamento só é executado durante o carregamento do checkpoint, portanto, substituir os módulos em um modelo já carregado deixa as novas camadas `q`, `k` e `v` com inicialização aleatória.

```py
from transformers import SamModel
from transformers.models.sam import modeling_sam

# replace the attention class the vision layers instantiate
modeling_sam.SAM_VISION_ATTENTION_CLASSES["eager"] = SamVisionAttentionSplit

# load the pretrained SAM model, the hook splits the qkv weights during loading
model = SamModel.from_pretrained("facebook/sam-vit-base", attn_implementation="eager")
```

## LoRA

Com projeções separadas de `q`, `k` e `v`, aplique LoRA a `q` e `v`.

Crie um [LoraConfig](https://huggingface.co/docs/peft/package_reference/config#peft.PeftConfig) e especifique o rank `r`, `lora_alpha`, `lora_dropout`, `task_type` e, mais importante, os módulos a serem alvo.

```py
from peft import LoraConfig, get_peft_model

config = LoraConfig(
    r=16,
    lora_alpha=32,
    # apply LoRA to q and v
    target_modules=["q", "v"],
    lora_dropout=0.1,
    task_type="FEATURE_EXTRACTION"
)
```

Passe o modelo e o [LoraConfig](https://huggingface.co/docs/peft/package_reference/config#peft.PeftConfig) para [get_peft_model](https://huggingface.co/docs/peft/package_reference/peft_model#peft.get_peft_model) para aplicar o LoRA ao modelo.

```py
model = get_peft_model(model, config)
```

Chame [print_trainable_parameters](https://huggingface.co/docs/peft/package_reference/peft_model#peft.PeftMixedModel.print_trainable_parameters) para visualizar o número de parâmetros que você está treinando em relação ao número total de parâmetros.

```py
model.print_trainable_parameters()
"trainable params: 589,824 || all params: 94,274,096 || trainable%: 0.6256"
```

