Transformers documentation
Personalización de modelos
Personalización de modelos
Los modelos de Transformers están diseñados para ser personalizables. El código de un modelo está completamente contenido en la subcarpeta model del repositorio de Transformers. Cada carpeta contiene un archivo modeling.py y un archivo configuration.py. Copia estos archivos para comenzar a personalizar un modelo.
Puede ser más fácil empezar desde cero si estás creando un modelo completamente nuevo. Pero para modelos que son muy similares a uno existente en Transformers, es más rápido reutilizar o subclasear la misma clase de configuración y de modelo.
Esta guía te mostrará cómo personalizar un modelo ResNet, habilitar el soporte de AutoClass y compartirlo en el Hub.
Configuración
Una configuración, definida por la clase base PreTrainedConfig, contiene toda la información necesaria para construir un modelo. Aquí es donde se configuran los atributos del modelo ResNet personalizado. Diferentes atributos producen diferentes tipos de modelos ResNet.
Las reglas principales para personalizar una configuración son:
- Una configuración personalizada debe ser una subclase de PreTrainedConfig. Esto garantiza que un modelo personalizado tenga toda la funcionalidad de un modelo de Transformers, como from_pretrained(), save_pretrained() y push_to_hub().
- El
__init__de PreTrainedConfig debe aceptar cualquierkwargsy estos deben pasarse al__init__de la superclase. PreTrainedConfig tiene más campos que los establecidos en su configuración personalizada, por lo que, al cargar una configuración con from_pretrained(), esos campos deben ser aceptados por su configuración y pasados a la superclase.
Es útil comprobar la validez de algunos de los parámetros. En el ejemplo siguiente, se implementa una comprobación para garantizar que
block_typeystem_typepertenezcan a uno de los valores predefinidos.Agregue
model_typea la clase de configuración para habilitar el soporte de AutoClass.
from transformers import PreTrainedConfig
from typing import List
class ResnetConfig(PreTrainedConfig):
model_type = "resnet"
def __init__(
self,
block_type="bottleneck",
layers: list[int] = [3, 4, 6, 3],
num_classes: int = 1000,
input_channels: int = 3,
cardinality: int = 1,
base_width: int = 64,
stem_width: int = 64,
stem_type: str = "",
avg_down: bool = False,
**kwargs,
):
if block_type not in ["basic", "bottleneck"]:
raise ValueError(f"`block_type` must be 'basic' or bottleneck', got {block_type}.")
if stem_type not in ["", "deep", "deep-tiered"]:
raise ValueError(f"`stem_type` must be '', 'deep' or 'deep-tiered', got {stem_type}.")
self.block_type = block_type
self.layers = layers
self.num_classes = num_classes
self.input_channels = input_channels
self.cardinality = cardinality
self.base_width = base_width
self.stem_width = stem_width
self.stem_type = stem_type
self.avg_down = avg_down
super().__init__(**kwargs)Guarde la configuración en un archivo JSON en su carpeta de modelo personalizado, custom-resnet, con save_pretrained().
resnet50d_config = ResnetConfig(block_type="bottleneck", stem_width=32, stem_type="deep", avg_down=True)
resnet50d_config.save_pretrained("custom-resnet")Modelo
Con la configuración personalizada de ResNet, ahora puede crear y personalizar el modelo. El modelo es una subclase de la clase base PreTrainedModel. Al igual que con PreTrainedConfig, heredar de PreTrainedModel e inicializar la superclase con la configuración extiende las funcionalidades de Transformers, como el guardado y la carga, al modelo personalizado.
Los modelos de Transformers siguen la convención de aceptar un objeto config en el método __init__. Esto pasa el objeto config completo a las subcapas del modelo, en lugar de dividir el objeto config en múltiples argumentos que se pasan individualmente a las subcapas.
Escribir modelos de esta manera produce un código más simple con una fuente de verdad clara para cualquier hiperparámetro. También facilita reutilizar código de otros modelos de Transformers.
Crearás dos modelos ResNet, un modelo ResNet básico que genera los estados ocultos y un modelo ResNet con una cabeza de clasificación de imágenes.
Defina un mapeo entre los tipos de bloques y las clases. Todo lo demás se crea pasando la clase de configuración a la clase de modelo ResNet.
Agrega
config_classa la clase del modelo para habilitar el soporte de AutoClass.
from transformers import PreTrainedModel
from timm.models.resnet import BasicBlock, Bottleneck, ResNet
from .configuration_resnet import ResnetConfig
BLOCK_MAPPING = {"basic": BasicBlock, "bottleneck": Bottleneck}
class ResnetModel(PreTrainedModel):
config_class = ResnetConfig
def __init__(self, config):
super().__init__(config)
block_layer = BLOCK_MAPPING[config.block_type]
self.model = ResNet(
block_layer,
config.layers,
num_classes=config.num_classes,
in_chans=config.input_channels,
cardinality=config.cardinality,
base_width=config.base_width,
stem_width=config.stem_width,
stem_type=config.stem_type,
avg_down=config.avg_down,
)
def forward(self, tensor):
return self.model.forward_features(tensor)El método forward debe reescribirse para calcular la pérdida de cada logit si hay etiquetas disponibles. En caso contrario, la clase de modelo ResNet es la misma.
Agrega
config_classa la clase del modelo para habilitar el soporte de AutoClass.
import torch
class ResnetModelForImageClassification(PreTrainedModel):
config_class = ResnetConfig
def __init__(self, config):
super().__init__(config)
block_layer = BLOCK_MAPPING[config.block_type]
self.model = ResNet(
block_layer,
config.layers,
num_classes=config.num_classes,
in_chans=config.input_channels,
cardinality=config.cardinality,
base_width=config.base_width,
stem_width=config.stem_width,
stem_type=config.stem_type,
avg_down=config.avg_down,
)
def forward(self, tensor, labels=None):
logits = self.model(tensor)
if labels is not None:
loss = torch.nn.functional.cross_entropy(logits, labels)
return {"loss": loss, "logits": logits}
return {"logits": logits}Un modelo puede devolver cualquier formato de salida. Devolver un diccionario (como ResnetModelForImageClassification) con pérdidas cuando hay etiquetas disponibles hace que el modelo personalizado sea compatible con Trainer. Para otros formatos de salida, necesitarás tu propio bucle de entrenamiento o una biblioteca diferente para el entrenamiento.
Instancia la clase de modelo personalizada con la configuración.
resnet50d = ResnetModelForImageClassification(resnet50d_config)
En este punto, puedes cargar pesos preentrenados en el modelo o entrenarlo desde cero. En esta guía, cargarás pesos preentrenados.
Cargue los pesos preentrenados desde la biblioteca timm y luego transfiera esos pesos al modelo personalizado con load_state_dict.
import timm
pretrained_model = timm.create_model("resnet50d", pretrained=True)
resnet50d.model.load_state_dict(pretrained_model.state_dict())AutoClass
La API AutoClass es una atajo para cargar automáticamente la arquitectura correcta de un modelo dado. Es conveniente habilitar esto para los usuarios que carguen su modelo personalizado.
Asegúrate de que la clase de configuración tenga el atributo model_type (debe ser diferente de los tipos de modelo existentes) y la clase de modelo tenga el atributo config_class. Usa el método register() para añadir la configuración y el modelo personalizados a la API de AutoClass.
El primer argumento de AutoConfig.register() debe coincidir con el atributo
model_typede la clase de configuración personalizada, y el primer argumento deAutoModel.register()debe coincidir con elconfig_classde la clase de modelo personalizada.
from transformers import AutoConfig, AutoModel, AutoModelForImageClassification
AutoConfig.register("resnet", ResnetConfig)
AutoModel.register(ResnetConfig, ResnetModel)
AutoModelForImageClassification.register(ResnetConfig, ResnetModelForImageClassification)El código de su modelo personalizado ahora es compatible con la API de AutoClass. Los usuarios pueden cargar el modelo con las clases AutoModel o AutoModelForImageClassification.
Subir
Sube un modelo personalizado al Hub para que otros usuarios puedan cargarlo y usarlo fácilmente.
Asegúrate de que el directorio del modelo esté estructurado correctamente, como se muestra a continuación. El directorio debe contener:
modeling.py: Contiene el código deResnetModelyResnetModelForImageClassification. Este archivo puede depender de importaciones relativas a otros archivos siempre que estén en el mismo directorio.
Al copiar un archivo de modelo de Transformers, reemplace todas las importaciones relativas en la parte superior del archivo
modeling.pypara importar desde Transformers en su lugar.
configuration.py: Contiene el código deResnetConfig.__init__.py: Puede estar vacío, este archivo permite que Python utiliceresnet_modelcomo un módulo.
.
└── resnet_model
├── __init__.py
├── configuration_resnet.py
└── modeling_resnet.pyPara compartir el modelo, importe el modelo y la configuración de ResNet.
from resnet_model.configuration_resnet import ResnetConfig
from resnet_model.modeling_resnet import ResnetModel, ResnetModelForImageClassificationCopia el código de los archivos de modelo y configuración. Para asegurarte de que los objetos AutoClass se guarden con save_pretrained(), llama al método register_for_auto_class(). Esto modifica el archivo JSON de configuración para incluir los objetos AutoClass y el mapeo.
Para un modelo, elige la clase AutoModelFor adecuada según la tarea.
ResnetConfig.register_for_auto_class()
ResnetModel.register_for_auto_class("AutoModel")
ResnetModelForImageClassification.register_for_auto_class("AutoModelForImageClassification")Para mapear más de una tarea al modelo, edite auto_map directamente en el archivo JSON de configuración.
"auto_map": {
"AutoConfig": "<your-repo-name>--<config-name>",
"AutoModel": "<your-repo-name>--<config-name>",
"AutoModelFor<Task>": "<your-repo-name>--<config-name>",
},Cree la configuración y el modelo y cargue los pesos preentrenados en él.
resnet50d_config = ResnetConfig(block_type="bottleneck", stem_width=32, stem_type="deep", avg_down=True)
resnet50d = ResnetModelForImageClassification(resnet50d_config)
pretrained_model = timm.create_model("resnet50d", pretrained=True)
resnet50d.model.load_state_dict(pretrained_model.state_dict())El modelo está listo para publicarse en el Hub. Inicia sesión en tu cuenta de Hugging Face desde la línea de comandos o el cuaderno.
hf auth login
from huggingface_hub import notebook_login
notebook_login()Llama a push_to_hub() en el modelo para subirlo al Hub.
resnet50d.push_to_hub("custom-resnet50d")Los pesos preentrenados, la configuración y los archivos modeling.py y configuration.py deben subirse ahora al Hub en un repositorio bajo tu espacio de nombres.
Dado que un modelo personalizado no utiliza el mismo código de modelado que un modelo de Transformers, es necesario añadir trust_remote_code=True en from_pretrained() para cargarlo. Consulta la sección de carga de modelos personalizados para obtener más información.