PEFT documentation
Solución de problemas
Solución de problemas
Si encuentras algún problema al usar PEFT, consulta la siguiente lista de problemas comunes y sus soluciones.
Los ejemplos no funcionan
Los ejemplos suelen depender de las versiones más recientes de los paquetes, así que asegúrese de que estén actualizados. En particular, compruebe las siguientes versiones de paquetes:
pefttransformersacceleratetorch
En general, puede actualizar la versión del paquete ejecutando este comando dentro de su entorno de Python:
python -m pip install -U <package_name>
Instalar PEFT desde el código fuente es útil para mantenerse al día con los últimos avances:
python -m pip install git+https://github.com/huggingface/peft
Problemas relacionados con Dtype
ValueError: Intentando desescalar gradientes FP16
Este error probablemente ocurrió porque el modelo se cargó con dtype=torch.float16 y luego se utilizó en un contexto de precisión mixta automática (AMP), por ejemplo, estableciendo fp16=True en la clase Trainer de 🤗 Transformers. La razón es que, al usar AMP, los pesos entrenables nunca deben usar fp16. Para que esto funcione sin cargar todo el modelo en fp32, añade lo siguiente a tu código:
peft_model = get_peft_model(...)
# add this:
for param in model.parameters():
if param.requires_grad:
param.data = param.data.float()
# proceed as usual
trainer = Trainer(model=peft_model, fp16=True, ...)
trainer.train()Alternativamente, puede usar la función cast_mixed_precision_params() para convertir los pesos correctamente:
from peft import cast_mixed_precision_params
peft_model = get_peft_model(...)
cast_mixed_precision_params(peft_model, dtype=torch.float16)
# proceed as usual
trainer = Trainer(model=peft_model, fp16=True, ...)
trainer.train()A partir de la versión v0.12.0 de PEFT, PEFT promueve automáticamente el dtype de los pesos del adaptador desde
torch.float16ytorch.bfloat16atorch.float32cuando es apropiado. Para evitar este comportamiento, puede pasarautocast_adapter_dtype=Falsea ~get_peft_model(), a from_pretrained() y a load_adapter().
Seleccionar el dtype del adaptador
La mayoría de los métodos de PEFT, como LoRA, funcionan añadiendo pesos de adaptador entrenables. De forma predeterminada, esos pesos se almacenan en el tipo de datos float32 (fp32), es decir, con una precisión relativamente alta. Por lo tanto, incluso si el modelo base se carga en float16 (fp16) o bfloat16 (bf16), los pesos del adaptador serán float32. Cuando se calculan los resultados del adaptador durante la pasada hacia adelante, la entrada suele estar en el tipo de datos del modelo base, por lo que se convertirá a float32 si es necesario y luego se volverá a convertir al tipo de datos original.
Si prefiere que los pesos del adaptador estén en la precisión inferior del modelo base, es decir, en float16 o bfloat16, puede pasar autocast_adapter_dtype=False al crear el modelo (~get_peft_model()) o al cargar el modelo (from_pretrained()). Esto tiene algunas ventajas y desventajas:
Ventajas del adaptador de media precisión:
- cálculo ligeramente más rápido
- menos memoria
- tamaño de archivo más pequeño del punto de control (la mitad del tamaño)
Desventajas del adaptador de media precisión:
- pérdida ligeramente peor
- mayor riesgo de desbordamiento o subdesbordamiento
Tenga en cuenta que, para la mayoría de los casos de uso, el tiempo de ejecución total y el costo de memoria estarán determinados por el tamaño del modelo base y por el conjunto de datos, mientras que el dtype del adaptador PEFT solo tendrá un pequeño impacto.
El entrenamiento se ejecuta, pero el modelo no mejora
Si el entrenamiento parece saludable (la pérdida disminuye y los puntos de control se guardan sin errores) pero el modelo resultante no rinde mejor que el modelo base, comprueba si los parámetros entrenables cambiaron realmente durante el entrenamiento:
# before training
sample_param = next(p for p in model.parameters() if p.requires_grad)
before = sample_param.detach().clone()
# ... train for at least three optimizer steps ...
after = next(p for p in model.parameters() if p.requires_grad)
print("weights updated:", not torch.allclose(before, after.detach().cpu()))Faltan hooks de forward o backward
Si los pesos no se movieron, una causa probable es que las referencias a los parámetros se capturaron antes de que los parámetros se materializaran en su dispositivo objetivo. Esto puede ocurrir cuando el modelo base se envuelve con get_peft_model() mientras sus parámetros aún no se han movido al dispositivo objetivo, y una biblioteca de terceros registra después hooks de forward/backward (por ejemplo, hooks de gradiente por muestra de un marco de privacidad diferencial) o se crea un optimizador antes de la primera llamada forward. Cuando los parámetros se materializan más tarde, esas referencias de hooks y del optimizador apuntan a tensores obsoletos y cada actualización se convierte en una operación sin efecto silenciosa.
Para evitarlo, utiliza el siguiente orden:
- Cargue el modelo base y muévalo al dispositivo de destino (p. ej.
model.to(device)). - Envuélvelo con get_peft_model().
- Solo entonces registra los hooks o crea el optimizador.
Los módulos objetivo no coinciden con el modelo
Si el entrenamiento se ejecuta sin errores pero la pérdida apenas cambia, o el modelo no muestra ninguna mejora después del ajuste fino, es posible que los target_modules configurados (o los target_modules predeterminados, si el usuario no especifica otros) no coincidan con los nombres reales de los módulos en el modelo. Esto es común en arquitecturas híbridas como Mamba, Jamba o NemotronH, que utilizan nombres de capas diferentes a los de los modelos Transformer estándar.
PEPT solo genera un error cuando ninguno de los target_modules configurados coincide con algún módulo del modelo. Si algunos coinciden y otros no, las entradas que no coinciden se omiten silenciosamente, sin ninguna advertencia. Una gran parte del modelo puede permanecer congelada sin ninguna indicación.
El primer diagnóstico consiste en comprobar el número de parámetros entrenables:
from peft import LoraConfig, get_peft_model
config = LoraConfig(target_modules=["q_proj", "v_proj", "gate_proj"])
model = get_peft_model(base_model, config)
model.print_healthcheck()Si el número reportado es mucho menor de lo esperado, inspeccione las capas del adaptador en el modelo:
print(model.get_layer_status())Esto devuelve una lista de entradas TunerLayerStatus que muestra el nombre, el tipo de módulo, el estado de activación y los adaptadores activos de cada capa de adaptador. Alternativamente, imprimir la representación del modelo (print(model)) proporciona una visión general de la jerarquía completa de módulos, lo cual es útil para identificar los nombres correctos de las capas en arquitecturas poco familiares.
Cualquier entrada de target_modules que no corresponda a un módulo del modelo se omite silenciosamente. Actualiza target_modules para que coincida con los nombres reales de los módulos.
Este problema afecta a todos los métodos de PEFT que utilizan
target_modules(LoRA, LoHa, IA³, etc.), no solo a LoRA. Los pasos de diagnóstico son los mismos independientemente del método. Si no está seguro de qué nombres de módulos utiliza una arquitectura determinada, consulte la documentación del modelo o inspeccionemodel.named_modules()antes de configurar el adaptador.
Resultados deficientes de un modelo PEFT cargado
Puede haber varias razones para obtener un resultado deficiente de un modelo PEFT cargado, que se enumeran a continuación. Si aún no puedes resolver el problema, comprueba si alguien más ha tenido un problema similar en GitHub y, si no encuentras ninguno, abre un nuevo problema.
Al abrir un problema, ayuda mucho si proporcionas un ejemplo de código mínimo que reproduzca el problema. Además, informa si el modelo cargado rinde al mismo nivel que el modelo antes del ajuste fino, si rinde a un nivel aleatorio o si es solo ligeramente peor de lo esperado. Esta información nos ayuda a identificar el problema más rápidamente.
Desviaciones aleatorias
Si las salidas de tu modelo no son exactamente las mismas que en ejecuciones anteriores, podría haber un problema con elementos aleatorios. Por ejemplo:
- asegúrese de que esté en modo
.eval(), lo cual es importante, por ejemplo, si el modelo usa dropout - si usa generate en un modelo de lenguaje, es posible que haya muestreo aleatorio, por lo que obtener el mismo resultado requiere establecer una semilla aleatoria
- si usó cuantización y fusionó los pesos, se esperan pequeñas desviaciones debido a errores de redondeo
Modelo cargado de forma incorrecta
Asegúrate de cargar el modelo correctamente. Un error común es intentar cargar un modelo entrenado con get_peft_model(), lo cual es incorrecto. En su lugar, el código de carga debe ser así:
from peft import PeftModel, PeftConfig
base_model = ... # to load the base model, use the same code as when you trained it
config = PeftConfig.from_pretrained(peft_model_id)
peft_model = PeftModel.from_pretrained(base_model, peft_model_id)Rutas de ejecución optimizadas que omiten los adaptadores
Algunas rutas de ejecución optimizadas leen los pesos del módulo directamente en lugar de llamar a sus métodos forward. Esto puede omitir los cambios realizados por adaptadores no fusionados sin emitir ninguna advertencia, incluso si el adaptador está activo. El problema puede afectar a diferentes módulos y métodos de PEFT.
Por ejemplo, el torch.nn.TransformerEncoder nativo de PyTorch puede usar una ruta fusionada durante la evaluación con torch.no_grad() o torch.inference_mode(). Esta ruta puede omitir adaptadores en los submódulos de TransformerEncoderLayer, como los adaptadores LoRA en linear1 y linear2. El entrenamiento puede funcionar correctamente mientras que la evaluación ignora los cambios del adaptador. Consulte issue #3976 para una reproducción.
Si sospechas de este problema, primero verifica que el adaptador esté habilitado y activo con get_layer_status(). Compara las salidas con las mismas entradas en modo de evaluación, con y sin el adaptador, y luego con la optimización deshabilitada. Utiliza un adaptador entrenado que modifique las salidas, ya que los adaptadores LoRA recién inicializados generalmente no tienen efecto. Las pequeñas diferencias numéricas son normales.
Para un adaptador no fusionado en un codificador nativo en modo eager, compare las salidas para la misma entrada de tensor x:
import torch
model.eval()
previous = torch.backends.mha.get_fastpath_enabled()
try:
with torch.inference_mode():
torch.backends.mha.set_fastpath_enabled(True)
fast_output = model(x)
torch.backends.mha.set_fastpath_enabled(False)
reference_output = model(x)
finally:
torch.backends.mha.set_fastpath_enabled(previous)
print((fast_output - reference_output).abs().max().item())Que una bandera de backend esté habilitada no significa por sí sola que se ejecute la ruta fusionada; su uso también depende del modelo, las entradas y las condiciones de tiempo de ejecución. Inspeccione la implementación de forward del módulo envolvente en busca de acceso directo a los pesos o llamadas a operadores fusionados. Tenga cuidado al depurar con hooks de forward: registrar un hook puede deshabilitar una optimización y cambiar el comportamiento que está investigando.
Para los codificadores nativos en modo eager, torch.backends.mha.set_fastpath_enabled(False) es una solución temporal que mantiene los adaptadores sin fusionar. Esta configuración es a nivel de proceso, puede afectar a otros modelos y al rendimiento, y no debe activarse o desactivarse de forma independiente por solicitudes concurrentes. Para otras rutas optimizadas, deshabilita la optimización afectada o utiliza una implementación que llame a los módulos adaptados. Si el método PEFT y la configuración de cuantización lo permiten, también puedes fusionar los adaptadores con merge_and_unload() y usar el modelo devuelto para la inferencia. Los cambios del adaptador se incluyen entonces en los pesos base. La fusión elimina los envoltorios del adaptador, por lo que conserva tus puntos de control originales si necesitas cambiar de adaptador o reanudar el entrenamiento.
Capas inicializadas aleatoriamente
Para algunas tareas, es importante configurar correctamente modules_to_save en la configuración para tener en cuenta las capas inicializadas aleatoriamente.
Como ejemplo, esto es necesario si usas LoRA para ajustar un modelo de lenguaje para clasificación de secuencias, porque 🤗 Transformers añade una cabeza de clasificación inicializada aleatoriamente sobre el modelo. Si no añades esta capa a modules_to_save, la cabeza de clasificación no se guardará. La próxima vez que cargues el modelo, obtendrás una cabeza de clasificación inicializada aleatoriamente distinta, lo que dará lugar a resultados completamente diferentes.
PEFT intenta adivinar correctamente modules_to_save si proporcionas el argumento task_type en la configuración. Esto debería funcionar para los modelos de transformers que siguen el esquema de nomenclatura estándar. Sin embargo, siempre es buena idea verificarlo dos veces, ya que no podemos garantizar que todos los modelos sigan dicho esquema.
Cuando cargas un modelo de transformers que tiene capas inicializadas aleatoriamente, deberías ver una advertencia similar a la siguiente:
Some weights of <MODEL> were not initialized from the model checkpoint at <ID> and are newly initialized: [<LAYER_NAMES>].
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.Las capas mencionadas deben agregarse a modules_to_save en la configuración para evitar el problema descrito.
healthcheck() informa de una advertencia TASK_HEAD_NOT_SAVED para la forma más común de este error: una cabeza de clasificación de secuencias, clasificación de tokens o respuesta a preguntas que se inicializó de nuevo al cargar el modelo base pero que no está en modules_to_save, por ejemplo, porque no se estableció task_type. La comprobación se omite si el modelo base no lista sus arquitecturas en la configuración.
Como ejemplo, al cargar un modelo que utiliza la arquitectura DeBERTa para la clasificación de secuencias, se mostrará una advertencia indicando que los siguientes pesos se han inicializado de nuevo:
['classifier.bias', 'classifier.weight', 'pooler.dense.bias', 'pooler.dense.weight']. De esto se deduce que las capasclassifierypoolerdeben añadirse a:modules_to_save=["classifier", "pooler"].
Extender el vocabulario
Para muchas tareas de ajuste fino de modelos de lenguaje, es necesario ampliar el vocabulario del modelo, ya que se introducen nuevos tokens. Esto requiere extender la capa de incrustaciones para tener en cuenta los nuevos tokens y, dependiendo del método de ajuste fino, también guardar la capa de incrustaciones además de los pesos del adaptador al guardar el adaptador. Hay varias formas de lograr esto, ordenadas por efectividad de parámetros:
- tokens entrenables, entrena solo los tokens especificados, opcionalmente almacena solo los valores actualizados
- entrenar un adaptador en la matriz de incrustaciones, opcionalmente almacenar solo los valores actualizados
- full-finetuning de la capa de embedding
Uso de tokens entrenables
Comencemos con los tokens entrenables, en este caso su integración con LoRA. Si solo te interesa entrenar los nuevos embeddings y nada más, consulta la documentación independiente.
Para habilitar el entrenamiento selectivo de tokens de la capa de embedding, deberá proporcionar los ids de los tokens que haya añadido a través del parámetro trainable_token_indices. Opcionalmente, puede especificar qué capa desea apuntar si hay más de una capa de embedding. Para un modelo Mistral, esto podría verse así:
new_tokens = ['<think>', '</think>']
tokenizer.add_tokens(new_tokens)
base_model.resize_token_embeddings(len(tokenizer))
lora_config = LoraConfig(
...,
trainable_token_indices={'embed_tokens': tokenizer.convert_tokens_to_ids(new_tokens)},
)Si tu modelo usa pesos vinculados (como lm_head), los tokens entrenables intentarán resolverlos y mantenerlos actualizados también, por lo que en ese caso no debería ser necesario añadir modules_to_save=["lm_head"]. Esto solo funciona si el modelo utiliza la convención de Transformers para vincular pesos.
Guardar el modelo con model.save_pretrained puede guardar la matriz de embeddings completa en lugar de solo la diferencia, como medida de precaución, porque la matriz de embeddings se redimensionó. Para ahorrar espacio, puedes deshabilitar este comportamiento estableciendo save_embedding_layers=False al llamar a save_pretrained. Esto es seguro siempre que no modifiques la matriz de embeddings por otros medios, ya que dichos cambios no se rastrean mediante tokens entrenables.
Usando un adaptador, p. ej. LoRA
Prepara la capa de embedding añadiéndola a target_modules de la configuración de tu adaptador. Por ejemplo, la configuración de Mistral podría verse así:
config = LoraConfig(..., target_modules=["embed_tokens", "lm_head", "q_proj", "v_proj"])Una vez que se añaden a target_modules, PEFT almacena automáticamente la capa de embedding al guardar el adaptador si el modelo tiene get_input_embeddings y get_output_embeddings. Esto suele ser el caso para los modelos de Transformers.
Si la capa de incrustación del modelo no sigue el esquema de nombres del Transformer pero de todos modos implementa get_input_embeddings, aún puedes guardarla pasando manualmente save_embedding_layers=True al guardar el adaptador:
model = get_peft_model(...)
# train the model
model.save_pretrained("my_adapter", save_embedding_layers=True)Para la inferencia, carga primero el modelo base y redimensiona de la misma manera que lo hiciste antes de entrenar el modelo. Después de redimensionar el modelo base, puedes cargar el punto de control de PEFT.
Para un ejemplo completo, consulte este cuaderno.
Ajuste fino completo
El ajuste fino completo es más costoso en términos de VRAM o espacio de almacenamiento, pero si todo lo demás falla, puedes recurrir a este método y ver si te funciona. Consíguelo añadiendo el nombre de la capa de embedding a modules_to_save. Ten en cuenta que también debes añadir las capas vinculadas, por ejemplo, lm_head. Ejemplo para un modelo Mistral con LoRA:
config = LoraConfig(..., modules_to_save=["embed_tokens", "lm_head"], target_modules=["q_proj", "v_proj"])Recibir una advertencia sobre “los pesos no están inicializados desde el punto de control del modelo”
Cuando cargas tu modelo PEFT que ha sido entrenado en una tarea (por ejemplo, clasificación), es posible que recibas una advertencia como:
Algunos pesos de LlamaForSequenceClassification no se inicializaron desde el punto de control del modelo en meta-llama/Llama-3.2-1B y se inicializaron de nuevo: [‘score.weight’]. Probablemente debería ENTRENAR este modelo en una tarea descendente para poder usarlo para predicciones e inferencia.
Aunque esto pueda parecer alarmante, lo más probable es que no haya motivo de preocupación. Esta advertencia proviene de Transformers y no es una advertencia específica de PEFT. Te informa que se ha adjuntado una cabeza de clasificación inicializada aleatoriamente (score) al modelo base, y que dicha cabeza debe entrenarse para producir predicciones coherentes.
Cuando recibes esta advertencia antes de entrenar el modelo, PEFT se encarga automáticamente de hacer que la cabeza de clasificación sea entrenable si pasaste correctamente el argumento task_type a la configuración de PEFT.
from peft import LoraConfig, TaskType
lora_config = LoraConfig(..., task_type=TaskType.SEQ_CLS)Si la cabeza de clasificación no sigue las convenciones de nomenclatura habituales de Transformers (lo cual es poco frecuente), debe indicar explícitamente a PEFT el nombre de la cabeza en modules_to_save.
lora_config = LoraConfig(..., modules_to_save=["name-of-classification-head"])Para comprobar el nombre de la cabeza de clasificación, imprima el modelo y debería ser el último módulo.
Si recibes esta advertencia desde tu código de inferencia, es decir, después de entrenar el modelo, al cargar el modelo PEFT, siempre debes cargar primero el modelo de Transformers. Dado que Transformers no sabe que cargarás los pesos de PEFT después, sigue emitiendo la advertencia.
Como siempre, es una buena práctica asegurarse de que el modelo funcione correctamente para la inferencia ejecutando algunas validaciones sobre él.
Ejecutar una comprobación de salud del entrenamiento
Antes de entrenar, use print_healthcheck() para obtener un resumen compacto y legible del estado del modelo PEFT. Se deriva de las APIs de estado del modelo y de las capas (ver la siguiente sección) y señala estados sospechosos, como adaptadores inconsistentes, adaptadores fusionados o un número sospechoso de parámetros entrenables.
peft_model.print_healthcheck()
Preste especial atención a la presencia de “Findings” al final del informe, que se incluye si se encontraron irregularidades.
Si prefiere una salida en formato de diccionario, que sea serializable en JSON, use healthcheck() en su lugar:
healthcheck = peft_model.healthcheck()
print(healthcheck.get("findings", "all good")Esta comprobación solo inspecciona el modelo y sus adaptadores. No valida tu bucle de entrenamiento, optimizador o conjunto de datos.
Comprobar el estado de las capas y del modelo
A veces, un modelo PEFT puede terminar en un estado incorrecto, especialmente al manejar múltiples adaptadores. Puede haber cierta confusión sobre qué adaptadores existen, cuál está activo, cuál está fusionado, etc. Para ayudar a investigar este problema, llama a los métodos get_layer_status() y get_model_status().
El método get_layer_status() le ofrece un resumen detallado de los adaptadores activos, fusionados y disponibles de cada capa objetivo.
>>> from transformers import AutoModel
>>> from peft import get_peft_model, LoraConfig
>>> model_id = "google/flan-t5-small"
>>> model = AutoModel.from_pretrained(model_id)
>>> model = get_peft_model(model, LoraConfig())
>>> model.get_layer_status()
[TunerLayerStatus(name='model.encoder.block.0.layer.0.SelfAttention.q',
module_type='lora.Linear',
enabled=True,
active_adapters=['default'],
merged_adapters=[],
requires_grad={'default': True},
available_adapters=['default']),
TunerLayerStatus(name='model.encoder.block.0.layer.0.SelfAttention.v',
module_type='lora.Linear',
enabled=True,
active_adapters=['default'],
merged_adapters=[],
requires_grad={'default': True},
available_adapters=['default']),
...]
>>> model.get_model_status()
TunerModelStatus(
base_model_type='T5Model',
adapter_model_type='LoraModel',
peft_types={'default': 'LORA'},
trainable_params=344064,
total_params=60855680,
num_adapter_layers=48,
enabled=True,
active_adapters=['default'],
merged_adapters=[],
requires_grad={'default': True},
available_adapters=['default'],
)En la salida del estado del modelo, debes buscar entradas que indiquen "irregular". Esto significa que PEFT detectó un estado inconsistente en el modelo. Por ejemplo, si merged_adapters="irregular", significa que al menos un adaptador se fusionó en algunos módulos objetivo pero no en otros. Como resultado, es muy probable que los resultados de inferencia sean incorrectos.
La mejor manera de resolver este problema es recargar el checkpoint completo del modelo y del adaptador. Asegúrate de no realizar operaciones incorrectas sobre el modelo, por ejemplo, fusionar manualmente adaptadores en algunos módulos pero no en otros.
Convierta el estado de la capa en un DataFrame de pandas para facilitar la inspección visual.
from dataclasses import asdict
import pandas as pd
df = pd.DataFrame(asdict(layer) for layer in model.get_layer_status())Es posible obtener esta información para modelos que no son PEFT si utilizan capas PEFT internamente, pero en ese caso no se puede determinar cierta información, como base_model_type o peft_types. Como ejemplo, puedes llamar a esto en un modelo de diffusers de la siguiente manera:
>>> import torch
>>> from diffusers import StableDiffusionPipeline
>>> from peft import get_model_status, get_layer_status
>>> path = "runwayml/stable-diffusion-v1-5"
>>> lora_id = "takuma104/lora-test-text-encoder-lora-target"
>>> pipe = StableDiffusionPipeline.from_pretrained(path, dtype=torch.float16)
>>> pipe.load_lora_weights(lora_id, adapter_name="adapter-1")
>>> pipe.load_lora_weights(lora_id, adapter_name="adapter-2")
>>> pipe.set_lora_device(["adapter-2"], "cuda")
>>> get_layer_status(pipe.text_encoder)
[TunerLayerStatus(name='text_model.encoder.layers.0.self_attn.k_proj',
module_type='lora.Linear',
enabled=True,
active_adapters=['adapter-2'],
merged_adapters=[],
requires_grad={'adapter-1': False, 'adapter-2': True},
available_adapters=['adapter-1', 'adapter-2'],
devices={'adapter-1': ['cpu'], 'adapter-2': ['cuda']}),
TunerLayerStatus(name='text_model.encoder.layers.0.self_attn.v_proj',
module_type='lora.Linear',
enabled=True,
active_adapters=['adapter-2'],
merged_adapters=[],
requires_grad={'adapter-1': False, 'adapter-2': True},
devices={'adapter-1': ['cpu'], 'adapter-2': ['cuda']}),
...]
>>> get_model_status(pipe.unet)
TunerModelStatus(
base_model_type='other',
adapter_model_type='None',
peft_types={},
trainable_params=797184,
total_params=861115332,
num_adapter_layers=128,
enabled=True,
active_adapters=['adapter-2'],
merged_adapters=[],
requires_grad={'adapter-1': False, 'adapter-2': True},
available_adapters=['adapter-1', 'adapter-2'],
devices={'adapter-1': ['cpu'], 'adapter-2': ['cuda']},
)Velocidad
La carga de los pesos del adaptador es lenta
La carga de adaptadores, como los pesos de LoRA, generalmente debería ser rápida en comparación con la carga del modelo base. Sin embargo, pueden existir casos de uso en los que los pesos del adaptador sean bastante grandes o en los que los usuarios necesiten cargar un gran número de adaptadores; en este caso, el tiempo de carga puede acumularse. La razón es que los pesos del adaptador se inicializan primero y luego se sobrescriben con los pesos cargados, lo cual es un desperdicio. Para acelerar el tiempo de carga, puedes pasar el argumento low_cpu_mem_usage=True a from_pretrained() y load_adapter().
Si esta opción funciona bien en diferentes casos de uso, es posible que se convierta en la opción predeterminada para la carga de adaptadores en el futuro.
Reproducibilidad
Modelos que usan batch norm
Al cargar un modelo PEFT entrenado cuya base utiliza batch norm (por ejemplo, torch.nn.BatchNorm1d o torch.nn.BatchNorm2d), es posible que no se puedan reproducir exactamente los mismos resultados. Esto se debe a que las capas de batch norm registran estadísticas de ejecución durante el entrenamiento, pero estas estadísticas no forman parte del checkpoint de PEFT. Por lo tanto, al cargar el modelo PEFT, se utilizarán las estadísticas de ejecución del modelo base (es decir, las de antes del entrenamiento con PEFT).
Dependiendo de tu caso de uso, esto puede no ser un gran problema. Sin embargo, si necesitas que tus salidas sean 100% reproducibles, puedes lograrlo añadiendo las capas de normalización por lote a modules_to_save. A continuación se muestra un ejemplo de esto usando resnet y LoRA. Ten en cuenta que establecemos modules_to_save=["classifier", "normalization"]. Necesitamos el argumento "classifier" porque nuestra tarea es clasificación de imágenes, y añadimos el argumento "normalization" para asegurarnos de que las capas de normalización por lote se guarden en el punto de control de PEFT.
from transformers import AutoModelForImageClassification
from peft import LoraConfig, get_peft_model
model_id = "microsoft/resnet-18"
base_model = AutoModelForImageClassification.from_pretrained(self.model_id)
config = LoraConfig(
target_modules=["convolution"],
modules_to_save=["classifier", "normalization"],
),Según el tipo de modelo que utilice, las capas de normalización de lote podrían tener nombres distintos de "normalization", por lo que asegúrese de que el nombre coincida con la arquitectura de su modelo.
Desajuste de versiones
Error al cargar la configuración debido a un argumento de palabra clave inesperado
Cuando encuentres un error como el que se muestra a continuación, significa que el adaptador que estás intentando cargar fue entrenado con una versión más reciente de PEFT que la versión que tienes instalada en tu sistema.
TypeError: LoraConfig.__init__() got an unexpected keyword argument <argument-name>La mejor manera de resolver este problema es instalar la última versión de PEFT:
python -m pip install -U PEFT
Si el adaptador se entrenó a partir de una instalación desde el código fuente de PEFT (una versión no publicada de PEFT), también es necesario instalar PEFT desde el código fuente.
python -m pip install -U git+https://github.com/huggingface/peft.git
Si no es posible actualizar PEFT, hay una solución alternativa que puede intentar.
Supongamos que el mensaje de error indica que el argumento de palabra clave desconocido se llama foobar. Busca dentro del adapter_config.json de este adaptador PEFT la entrada foobar y elimínala del archivo. Luego guarda el archivo e intenta cargar el modelo de nuevo.
Esta solución funciona la mayor parte del tiempo. Mientras sea el valor predeterminado de foobar, se puede ignorar. Sin embargo, cuando se establece en algún otro valor, se obtendrán resultados incorrectos. La actualización de PEFT es la solución recomendada.
Gestión de adaptadores
Uso de múltiples adaptadores al mismo tiempo
PEPT te permite crear más de un adaptador en el mismo modelo. Esto puede ser útil en muchas situaciones. Por ejemplo, para inferencia, es posible que quieras servir dos modelos afinados a partir del mismo modelo base en lugar de cargar el modelo base una vez para cada modelo afinado, lo cual consumiría más memoria. Sin embargo, se pueden activar varios adaptadores al mismo tiempo. De esta manera, el modelo puede aprovechar los aprendizajes de todos esos adaptadores simultáneamente. Como ejemplo, si tienes un modelo de difusión, es posible que quieras usar un adaptador LoRA para cambiar el estilo y otro diferente para cambiar el sujeto.
Activar varios adaptadores al mismo tiempo es generalmente posible en todos los métodos de PEFT (LoRA, LoHa, IA³, etc.), excepto en los métodos de prompt learning (p-tuning, prefix tuning, etc.). El siguiente ejemplo ilustra cómo lograr esto:
from transformers import AutoModelForCausalLM
from peft import PeftModel
model_id = ...
base_model = AutoModelForCausalLM.from_pretrained(model_id)
model = PeftModel.from_pretrained(base_model, lora_path_0) # default adapter_name is 'default'
model.load_adapter(lora_path_1, adapter_name="other")
# the 'other' adapter was loaded but it's not active yet, so to activate both adapters:
model.base_model.set_adapter(["default", "other"])En el ejemplo anterior, se puede ver que es necesario llamar a
model.base_model.set_adapter(["default", "other"]). ¿Por qué no podemos llamar amodel.set_adapter(["default", "other"])? Lamentablemente, esto no es posible porque, como se explicó anteriormente, algunos métodos de PEFT no admiten la activación de más de un adaptador a la vez.
También es posible entrenar dos adaptadores al mismo tiempo, pero debes tener cuidado de asegurarte de que los pesos de ambos adaptadores sean conocidos por el optimizador. De lo contrario, solo un adaptador recibirá actualizaciones.
from transformers import AutoModelForCausalLM
from peft import LoraConfig, get_peft_model
model_id = ...
base_model = AutoModelForCausalLM.from_pretrained(model_id)
lora_config_0 = LoraConfig(...)
lora_config_1 = LoraConfig(...)
model = get_peft_model(base_model, lora_config_0)
model.add_adapter(adapter_name="other", peft_config=lora_config_1)Si ahora llamamos a:
from transformers import Trainer
trainer = Trainer(model=model, ...)
trainer.train()o
optimizer = torch.optim.AdamW([param for param in model.parameters() if param.requires_grad], ...)entonces el segundo adaptador LoRA ("other") no se entrenaría. Esto se debe a que en este momento está inactivo, lo que significa que el atributo requires_grad de sus parámetros está establecido en False y el optimizador lo ignorará. Por lo tanto, asegúrese de activar todos los adaptadores que deben entrenarse antes de inicializar el optimizador:
# activate all adapters
model.base_model.set_adapter(["default", "other"])
trainer = Trainer(model=model, ...)
trainer.train()Esta sección trata sobre el uso de múltiples adaptadores del mismo tipo en el mismo modelo, por ejemplo, el uso de varios adaptadores LoRA al mismo tiempo. No se aplica al uso de tipos diferentes de adaptadores en el mismo modelo, por ejemplo, un adaptador LoRA y un adaptador LoHa. Para ello, consulte
PeftMixedModel.
Fusionar solo algunos de los adaptadores activos
Cuando los pesos del adaptador se fusionan con los pesos base, las capas afectadas ya no aplican ningún adaptador durante la pasada hacia adelante, solo utilizan los pesos base fusionados. Por lo tanto, si varios adaptadores están activos pero solo algunos se fusionan, los adaptadores que siguen activos pero no fusionados no se aplican de forma silenciosa:
model = get_peft_model(base_model, lora_config_0, adapter_name="default")
model.add_adapter("other", lora_config_1)
model.base_model.set_adapter(["default", "other"])
output_both = model(**inputs)
model.merge_adapter(adapter_names=["default"])
output_merged = model(**inputs) # this is *not* the same as output_bothAquí, output_merged es la salida del adaptador "default" por sí solo, aunque "other" siga listado como activo:
>>> model.get_layer_status()[0].active_adapters
['default', 'other']
>>> model.get_layer_status()[0].merged_adapters
['default']Lo mismo ocurre cuando se fusiona un adaptador que no está activo: a partir de ese momento, el modelo devuelve la salida del adaptador fusionado y el activo se ignora. En ninguno de los dos casos se genera un error ni una advertencia. Los pesos del adaptador en sí no se ven afectados, por lo que llamar a unmerge_adapter() restaura la salida combinada.
Para evitarlo, fusiona todos los adaptadores activos, es decir, llama a model.merge_adapter() sin adapter_names, o desfusiona antes de cambiar qué adaptadores están activos.
Este comportamiento es coherente en todos los métodos de PEFT que admiten la fusión (LoRA, LoHa, LoKr, IA³, OFT, etc.), ya que todos comparten la misma lógica en la pasada hacia adelante.
Update on GitHubEste comportamiento resulta confuso y se planea cambiarlo en la versión v1.0 de PEFT.