Transformers documentation
Escrevendo kernels
Escrevendo kernels
Este guia explica como escrever kernels que vão além de uma substituição forward sem estado. Ele aborda duas capacidades suportadas pela API estendida KernelConfig:
- Transformação de parâmetros: o kernel espera pesos em um layout diferente do modelo original (por exemplo, parâmetros renomeados ou mesclados).
- Fusão de módulos: o kernel substitui múltiplos módulos adjacentes por uma única implementação fundida.
Para kernels básicos (substituições de forward sem estado, sem alterações de parâmetros), consulte a documentação da biblioteca kernels.
Padrão de duas classes
Qualquer kernel que carrega seus próprios parâmetros segue um padrão de duas classes.
KernelName: contém apenas a passagemforward. A bibliotecakernelsusa essa classe para kernelizar o modelo, pois não permite classes de kernel com estado.KernelNameLayout: umnn.Moduleque armazena os parâmetros e aplica monkey-patching no módulo original antes que o checkpoint seja carregado. Em tempo de execução,kernelizesubstitui seuforwardpeloforwarddeKernelName. Você não precisa definirforward. O Transformers injeta um automaticamente com a mesma assinatura deKernelName.forward.
A convenção de nomenclatura é rigorosa. A classe de layout deve ser nomeada
{KernelName}Layoute definida no mesmo módulo deKernelName.
Transformação de parâmetros
Use este padrão quando o kernel espera pesos com nomes diferentes ou em uma forma diferente da do checkpoint original do modelo.
A classe KernelNameLayout possui a mesma assinatura de __init__ que o módulo que substitui e declara um atributo de classe conversion_mapping que informa ao Transformers como remapear as chaves do checkpoint para os novos nomes de parâmetros (veja Carregamento dinâmico de pesos para mais detalhes).
# layers.py
try:
from transformers import Concatenate, WeightConverter
from transformers.conversion_mapping import WeightRenaming
except ImportError:
Concatenate = WeightConverter = WeightRenaming = None
import torch
import torch.nn as nn
class CustomRMSNormLayout(nn.Module):
conversion_mapping = [...] # rules that remap checkpoint keys to the new parameter names
def __init__(self, hidden_size: int, eps: float = 1e-6):
super().__init__()
self.scale = nn.Parameter(torch.ones(hidden_size))
self.variance_epsilon = eps
class CustomRMSNorm(nn.Module):
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
input_dtype = hidden_states.dtype
hidden_states = hidden_states.to(torch.float32)
variance = hidden_states.pow(2).mean(-1, keepdim=True)
hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
return self.scale * hidden_states.to(input_dtype)# __init__.py
from . import layersA biblioteca
kernelsdescobre as classes de kernel por meio de um módulolayers(sejalayers.pyoulayers/__init__.py). Defina as classes de kernel e de layout diretamente emlayers.pye exponha o módulolayersa partir do__init__.pydo pacote. O Transformers encontraCustomRMSNormLayoutno mesmo módulo deCustomRMSNorm.Proteja as importações específicas do Transformers (
WeightConverter,Concatenate,WeightRenaming) emlayers.pypara que o kernel também possa ser importado em ambientes em que o Transformers não está instalado.
Carregue este kernel passando o repositório e o nome da classe para KernelConfig. A chave é o nome da classe do módulo original do modelo. O valor aponta para a classe KernelName (não para o Layout) no repositório.
O valor é uma string "owner/repo:ClassName" ou uma tupla (repo_string, metadata). No formato de tupla, passe version ou revision (pelo menos um) e, opcionalmente, trust_remote_code. Use version para uma versão principal do kernel ou revision para uma tag, branch ou commit. Defina trust_remote_code=True para carregar de fora da organização confiável kernels-community. Uma string simples tem como padrão version=1 e trust_remode_code=False.
from transformers import AutoModelForCausalLM, KernelConfig
kernel_config = KernelConfig({"RMSNorm": ("owner/my-kernel:CustomRMSNorm", {"trust_remote_code": True, "version": 1})})
model = AutoModelForCausalLM.from_pretrained(
"Qwen/Qwen3-0.6B",
use_kernels=True,
kernel_config=kernel_config,
device_map="auto",
)Quando o modelo é carregado, o Transformers:
- Carrega
CustomRMSNormdo repositório e procura porCustomRMSNormLayoutno mesmo módulo. - Aplica monkey-patch em cada
RMSNormdo modelo comCustomRMSNormLayout. - Remapeia os pesos do checkpoint usando
conversion_mappingpara que sejam carregados nos novos nomes de parâmetros. - Chama
kernelize, que substituiCustomRMSNormLayout.forwardporCustomRMSNorm.forward.
Fusão de módulos
Use este padrão quando um kernel substitui vários módulos adjacentes por uma única implementação fundida. Como o módulo fundido combina parâmetros de vários módulos originais, o KernelNameLayout.__init__ recebe os módulos filhos instanciados, em vez dos argumentos de seus construtores.
# layers.py
try:
from transformers import Concatenate, WeightConverter
from transformers.conversion_mapping import WeightRenaming
except ImportError:
Concatenate = WeightConverter = WeightRenaming = None
import torch
import torch.nn as nn
class RMSNormMLPLayout(nn.Module):
conversion_mapping = [...] # rules that remap checkpoint keys to the fused parameter names
def __init__(self, norm, mlp):
super().__init__()
self.variance_epsilon = norm.variance_epsilon
self.scale = nn.Parameter(torch.empty_like(norm.weight))
self.gate_up_proj = nn.Linear(
mlp.gate_proj.in_features,
mlp.gate_proj.out_features + mlp.up_proj.out_features,
bias=mlp.gate_proj.bias is not None,
device=mlp.gate_proj.weight.device,
dtype=mlp.gate_proj.weight.dtype,
)
self.down_proj = nn.Linear(
mlp.down_proj.in_features,
mlp.down_proj.out_features,
bias=mlp.down_proj.bias is not None,
device=mlp.down_proj.weight.device,
dtype=mlp.down_proj.weight.dtype,
)
self.act_fn = mlp.act_fn
class RMSNormMLP(nn.Module):
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
input_dtype = hidden_states.dtype
hidden_states = hidden_states.to(torch.float32)
variance = hidden_states.pow(2).mean(-1, keepdim=True)
hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
hidden_states = self.scale * hidden_states.to(input_dtype)
gate, up = self.gate_up_proj(hidden_states).chunk(2, dim=-1)
return self.down_proj(self.act_fn(gate) * up)# __init__.py
from . import layersPara fundir módulos, passe uma tupla de pares (class_name, path_pattern) como chave em KernelConfig em vez de uma string simples. Todos os padrões devem compartilhar o mesmo módulo pai (o Transformers funde os filhos nesse pai). O curinga * corresponde a qualquer segmento único de caminho.
from transformers import AutoModelForCausalLM, KernelConfig
kernel_config = KernelConfig(
{
(
("RMSNorm", "model.layers.*.post_attention_layernorm"),
("MLP", "model.layers.*.mlp"),
): ("owner/my-kernel:RMSNormMLP", {"trust_remote_code": True, "version": 1}),
}
)
model = AutoModelForCausalLM.from_pretrained(
"Qwen/Qwen3-0.6B",
use_kernels=True,
kernel_config=kernel_config,
device_map="auto",
)Quando o modelo é carregado, o Transformers:
- Carrega
RMSNormMLPdo repositório e localizaRMSNormMLPLayoutno mesmo módulo. - Corresponde a cada camada do decoder em
model.layers.*e constrói uma classe pai fundida cujo__init__chamaRMSNormMLPLayout(post_attention_layernorm, mlp). - Substitui o filho restante (
mlp) pornn.Identity()para preservar a interface do módulo pai. - Remapeia os pesos do checkpoint usando
conversion_mapping. - Chama
kernelize, que substituiRMSNormMLPLayout.forwardporRMSNormMLP.forward.
Update on GitHubA ordem dos pares na tupla de fusão determina a ordem dos argumentos passados para
KernelNameLayout.__init__.