LoRA adapters¶
Log a PEFT LoRA adapter as its own Model Registry version, linked to the base model version it was trained on. Serve many adapters against that base on one GPU vLLM service, and pick which adapter answers each query.
An adapter version stores adapter weights only. The base version still holds the full model.
Limitations¶
- Adapters are served on a vLLM service in Snowpark Container Services. They can’t run in a warehouse.
- Only LoRA adapters are supported.
- A service serves adapters for one base model version. To change its adapters, recreate the service.
Log an adapter¶
:class:~snowflake.ml.model.PeftAdapter takes a base_model (ModelVersion) and exactly one source:
adapter_path: a local directory.adapter: an in-memorypeft.PeftModel. Requirespeft.adapter_repo: a Hugging Face Hub repo. Requireshuggingface_hub.revisionandtoken(private repos) apply only here.
subfolder works with adapter_path and adapter_repo. The source must contain adapter_config.json.
The adapter version uses the same methods and signatures as its base, so do not pass signatures,
sample_input_data, or target_platforms other than SNOWPARK_CONTAINER_SERVICES.
Make sure the adapter was trained on the base model version you specify.
Supported base versions¶
base_model must be a full-weight model version in your account, or a Cortex Base Model version (CORTEX_BASE).
For a Cortex Base Model, log adapter versions under your own model.
A full-weight base must have a model parameter on __call__, which callers use to select an adapter. A Hugging
Face text-generation pipeline with a chat template gets this signature by default. Otherwise, pass it when you
log the base:
__call__ then takes:
- Input:
messages - Parameters:
temperature,max_completion_tokens,stop,n,stream,top_p,frequency_penalty,presence_penalty,response_format, andmodel(optional string; omit it to call the base model)
To check an existing version, call base.show_functions(). If __call__ has no model parameter, as with
OPENAI_CHAT_SIGNATURE, log the model again with OPENAI_CHAT_WITH_PARAMS_SIGNATURE.
View an adapter’s base version¶
The link between an adapter and its base version is recorded as lineage.
If you don’t have a privilege on the linked model, GET_LINEAGE shows it as MASKED.
Create a service with adapters¶
Pass adapters on create_service as a dict from alias to adapter version.
You can also pass a list of adapter versions. Omit adapters, or pass {} or [], for a service with no
adapters.
To serve a single adapter, create the service on the adapter version. This deploys its base version with only that adapter attached.
Every adapter must have the deployed version as its base, and each adapter version can be attached once. For the names callers use to select each adapter, see Adapter names.
To set LoRA capacity, pass --max-loras (default 8) or --max-lora-rank (default: the largest rank among the
attached adapters) in engine_args_override. Snowflake configures LoRA serving and adapter loading for you.
Each service instance loads every attached adapter when it starts, including after a restart or scale-out. If an adapter cannot be loaded, that instance does not become ready.
Select an adapter per request¶
The adapter is selected by model, the same field OpenAI-compatible clients already send.
Adapter names¶
Every attached adapter can be selected by its fully qualified version name,
<database>.<schema>.<model>/VERSIONS/<version> (for example, ML.DEMOS.SUPPORT_TONE/VERSIONS/V1). An alias you
set is an additional name for the same adapter.
| How the adapter is attached | Values of model that select it |
|---|---|
adapters={"support": support_mv} | support or ML.DEMOS.SUPPORT_TONE/VERSIONS/V1 |
adapters=[support_mv] | ML.DEMOS.SUPPORT_TONE/VERSIONS/V1 |
support_mv.create_service(...) or support_mv.run_batch(...) | ML.DEMOS.SUPPORT_TONE/VERSIONS/V1 |
inference.adapters item with alias: support | support or ML.DEMOS.SUPPORT_TONE/VERSIONS/V1 |
inference.adapters item without alias | ML.DEMOS.SUPPORT_TONE/VERSIONS/V1 |
To call the base model, omit model. From an OpenAI-compatible client, which always sends model, set it to the
base model’s name.
Aliases must be unique and non-empty, and are case-sensitive. In SQL, one row with an unmatched model fails the
whole query.
Examples¶
In SQL, model can be a constant or a column, so each row can use a different adapter:
With the REST API:
OpenAI-compatible /v1/chat/completions: set model to the alias. GET /v1/models lists every value model
accepts.
Python against an existing service:
You can also call run(..., service_name=...) on an adapter version.
See which adapters are on a service¶
The result lists each attached adapter’s name, version, and alias. You need the same privileges as to call
the service.
To list every adapter logged against a base, whether or not it is attached to a service, use
base.get_adapters().
Batch inference jobs¶
Batch inference jobs take the same adapters
value on InferenceSpec.adapters. Set input_spec.params["model"] to choose
one adapter for the whole job.
You can also call run_batch on an adapter version. To score the adapter, set params.model to its fully
qualified version name (see Adapter names). If omitted, the base model is used.
SQL EXECUTE INFERENCE JOB SERVICE takes the same list under inference.adapters. alias is optional:
Batch jobs with adapters require the vLLM engine. If the job’s MODEL is an adapter version, the job runs on its
base version with that adapter attached, and inference.adapters must be omitted.
Privileges¶
The base model, each adapter model, and the service each have their own privileges. For example, a role that can read the base model can’t read its adapters unless it’s granted access to them too. Privileges are granted on a model and cover all of its versions.
| When | Who is checked | What they need |
|---|---|---|
| Log an adapter | Logger | CREATE MODEL on the target schema. USAGE, READ, or OWNERSHIP on the base model. |
| Create the service | Deployer | READ or OWNERSHIP on a base in your account (USAGE for a Cortex Base Model). READ or OWNERSHIP on each attached adapter. CREATE SERVICE plus the usual deploy dependencies. |
| Call the service | Caller | USAGE on the service and GRANT SERVICE ROLE …!INFERENCE_SERVICE_FUNCTION_USAGE. No adapter grant. |
| List attached adapters | Caller | Same as call. |
Access to an adapter is checked when it is attached to a service. After that, any role that can call the service can use any attached adapter, without a grant on the adapter model.
A common setup: a platform role owns the base and the service; a product role owns the adapter and grants
READ to the deployer.
When an instance restarts or scales out, it loads the base and adapters with the service owner role, not the
deployer. If that role loses READ on the base or an adapter model, including after an ownership transfer, new
instances do not become ready.
Related¶
- Hugging Face pipeline — full-weight Hugging Face models
- Deploy models for real-time inference (REST API)
- Batch inference jobs
- ML Lineage: Trace ML data flow
- snowflake.ml.model.ModelVersion