Transformers documentation

Tùy chỉnh các thành phần của mô hình

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v5.22.2).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Tùy chỉnh các thành phần của mô hình

Một cách khác để tùy chỉnh mô hình là sửa đổi các thành phần của chúng, thay vì viết một mô hình hoàn toàn mới, cho phép bạn điều chỉnh mô hình cho phù hợp với trường hợp sử dụng cụ thể của mình. Ví dụ, bạn có thể thêm các lớp mới hoặc tối ưu hóa cơ chế chú ý của một kiến trúc. Các tùy chỉnh được áp dụng trực tiếp lên mô hình Transformers để bạn có thể tiếp tục sử dụng các tính năng như Trainer, PreTrainedModel và thư viện PEFT.

Hướng dẫn này sẽ cho bạn thấy cách tùy chỉnh cơ chế chú ý của một mô hình để áp dụng Low-Rank Adaptation (LoRA) cho nó.

Tiện ích clear_import_cache rất hữu ích khi bạn đang sửa đổi và phát triển mã mô hình theo từng lần lặp. Nó xóa tất cả các module Transformers đã được cache và cho phép Python tải lại mã đã sửa mà không cần liên tục khởi động lại môi trường của bạn.

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")

Lớp Attention

Segment Anything là một mô hình phân đoạn hình ảnh, và nó kết hợp phép chiếu query-key-value (qkv) trong các cơ chế chú ý của mình. Để giảm số lượng tham số có thể huấn luyện và chi phí tính toán, bạn có thể áp dụng LoRA cho phép chiếu qkv. Điều này đòi hỏi phải tách phép chiếu qkv để bạn có thể nhắm mục tiêu riêng biệt vào q và v bằng LoRA.

  1. Tạo một lớp attention tùy chỉnh, SamVisionAttentionSplit, bằng cách kế thừa từ lớp SamVisionAttention gốc. Trong __init__, xóa qkv kết hợp và tạo một lớp tuyến tính riêng biệt cho q, k và v.
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)
  1. Hàm split_q_k_v_load_hook tách các trọng số qkv được huấn luyện trước thành các trọng số q, k và v riêng biệt khi tải mô hình, nhằm đảm bảo tính tương thích với bất kỳ mô hình huấn luyện trước nào.
    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]
  1. Trong bước forward, q, k và v được tính toán riêng biệt trong khi phần còn lại của cơ chế chú ý vẫn giữ nguyên.
    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

Gán lớp SamVisionAttentionSplit tùy chỉnh vào module SamVisionAttention của mô hình gốc để thay thế nó. Tất cả các instance của SamVisionAttention trong mô hình sẽ được thay bằng phiên bản attention tách.

Thay thế lớp trước khi gọi from_pretrained(). Hook tải chỉ chạy trong khi checkpoint đang được tải, do đó việc thay thế các module trên một model đã được tải sẽ khiến các lớp q, k và v mới được khởi tạo ngẫu nhiên.

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

Với các phép chiếu q, k và v riêng biệt, hãy áp dụng LoRA cho q và v.

Tạo một LoraConfig và chỉ định rank r, lora_alpha, lora_dropout, task_type, và quan trọng nhất, các module cần nhắm tới.

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"
)

Truyền model và LoraConfig vào get_peft_model để áp dụng LoRA cho model.

model = get_peft_model(model, config)

Gọi print_trainable_parameters để xem số lượng tham số bạn đang huấn luyện so với tổng số tham số.

model.print_trainable_parameters()
"trainable params: 589,824 || all params: 94,274,096 || trainable%: 0.6256"
Update on GitHub