Transformers documentation
Tùy chỉnh các thành phần của mô hình
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.
- Tạo một lớp attention tùy chỉnh,
SamVisionAttentionSplit, bằng cách kế thừa từ lớpSamVisionAttentiongốc. Trong__init__, xóaqkvkết hợp và tạo một lớp tuyến tính riêng biệt choq,kvà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)- Hàm
split_q_k_v_load_hooktách các trọng sốqkvđược huấn luyện trước thành các trọng sốq,kvàvriê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]- Trong bước
forward,q,kvà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 outputsGá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"