{"course":{"slug":"hugging-face","title":"Hugging Face, End to End","url":"https://addaly.com/learn/hugging-face","level":"intermediate","track":"create"},"lesson":{"slug":"publishing-an-adapter","title":"Shipping the thing you trained","url":"https://addaly.com/learn/hugging-face/publishing-an-adapter","position":65,"of":75,"minutes":8,"answer":"Publish the adapter rather than a merged republication of somebody else's weights, record the base model, training configuration and a measured result against a baseline in the card, and keep the dataset and evaluation set.","keyIdea":"Publish the adapter rather than a merged republication of somebody else's weights, record the base model, training configuration and a measured result against a baseline in the card, and keep the dataset and evaluation set, because the adapter expires when its base is superseded and the data does not."},"body":{"format":"markdown","text":"## Push the adapter, not the merged model\n\n```python\nmodel.push_to_hub(\"your-name/support-hi-lora\")\ntok.push_to_hub(\"your-name/support-hi-lora\")\n```\n\nA LoRA adapter is a few megabytes. The merged model is several gigabytes of mostly somebody else's weights, republished. Publish the adapter:\n\n- It is honest about what you made.\n- It is fast to download and cheap to store.\n- It stays usable when the base is updated, and it makes clear which base it needs.\n- Several adapters can share one loaded base at serving time.\n\nPublish a merged version as well only when there is a reason: a serving stack that cannot load adapters, or a GGUF conversion for people running on CPUs. Say in the card that it is a merge of your adapter and name the base.\n\n## The adapter card\n\n`adapter_config.json` records `base_model_name_or_path`, and the Hub uses it to link your repository to its parent. Add the metadata that makes it findable and usable:\n\n```yaml\n---\nbase_model: Qwen/Qwen2.5-7B-Instruct\nlibrary_name: peft\ntags:\n  - lora\n  - sft\nlanguage:\n  - hi\n  - en\nlicense: apache-2.0\ndatasets:\n  - your-name/support-conversations\n---\n```\n\nThe `license` field needs a moment's thought and the next module covers it properly. The short version: **your adapter inherits obligations from the base model's licence.** A LoRA trained on a model with a restrictive licence does not become permissive because you wrote Apache 2.0 in your own front matter. State the base's terms as well as your own.\n\n## What the prose must contain\n\nSix things, and a card without them is not usable by a stranger:\n\n1. **A runnable snippet**, with the base model named and the revision pinned:\n\n   ```python\n   from peft import PeftModel\n   from transformers import AutoModelForCausalLM, AutoTokenizer\n\n   base = AutoModelForCausalLM.from_pretrained(\"Qwen/Qwen2.5-7B-Instruct\",\n                                               dtype=\"bfloat16\", device_map=\"auto\")\n   model = PeftModel.from_pretrained(base, \"your-name/support-hi-lora\")\n   tok = AutoTokenizer.from_pretrained(\"your-name/support-hi-lora\")\n   ```\n\n2. **What it was trained on**: how many examples, from where, in what languages, and the dataset repository if you can publish it.\n3. **The training configuration**: rank, alpha, target modules, learning rate, epochs, base precision. Six lines, and without them nobody can reproduce or extend your work.\n4. **A measured result.** Even \"macro-F1 0.78 on 300 held-out tickets, against 0.61 for the base model\" is infinitely better than \"works well\". Say what the baseline was.\n5. **A named limitation.** The thing it does badly, from your own testing. Every model has one and a card without one reads as untested.\n6. **Whether the training data contained personal information**, and what you did about it.\n\n## Before you push\n\n- **Check for secrets in the output directory.** A `.env`, a notebook with a token in a cell, a config with a connection string. `git status` in `output_dir` before uploading, and use `ignore_patterns` on `upload_folder`.\n- **Check for training data you cannot publish.** Checkpoint directories sometimes contain a copy of the tokenised dataset.\n- **Start private.** Make it public when you have read your own card back.\n\n## Serving several adapters on one base\n\n```python\nmodel.load_adapter(\"your-name/support-hi-lora\", adapter_name=\"hi\")\nmodel.load_adapter(\"your-name/support-ta-lora\", adapter_name=\"ta\")\nmodel.set_adapter(\"hi\")\n```\n\nOne base in memory, several behaviours, switched per request. For a product serving several languages or several customers, this is the architecture that makes fine-tuning affordable: one 14 GB base and a directory of 20 MB adapters, rather than one 14 GB model per variant.\n\n## The maintenance you are signing up for\n\nAn adapter is pinned to its base. When a better base arrives in a few months, your adapter does not transfer: the weight shapes may match and the behaviour will not, because the thing it learned to correct has changed. You retrain.\n\nThis is why the dataset matters more than the adapter. Keep the training data, the configuration and the evaluation set in a repository, and retraining against a new base is an afternoon. Keep only the adapter and you are starting again.\n\n## Do this now\n\nPublish an adapter privately, then open it in a fresh Colab session and load it from the card's snippet alone. If it does not work, your card is wrong, and that is exactly the test every model card should have to pass before it goes public."},"check":{"question":"Why is publishing a LoRA adapter generally preferable to publishing the merged full model?","options":[{"text":"Merged models lose the improvement, because merging approximates the adapter's effect rather than reproducing it.","correct":false,"tempting":"Merging is exact arithmetic for the trained precision; the approximation concern applies narrowly to QLoRA."},{"text":"Adapters can be applied to any base model of the same size, which merged weights cannot.","correct":false,"tempting":"Adapters are tied to their exact base; shape compatibility is not behavioural compatibility."},{"text":"It is a few megabytes, states which base it needs, and lets one loaded base serve several adapters.","correct":true},{"text":"The Hub charges for storage above a threshold, so large merged repositories incur costs adapters avoid.","correct":false,"tempting":"Public repository storage is not the constraint that drives this choice."}]},"prev":{"slug":"what-goes-wrong-in-training","title":"Loss is NaN, memory is gone, nothing is learning","url":"https://addaly.com/learn/hugging-face/what-goes-wrong-in-training"},"next":{"slug":"tokens-and-gates","title":"A token in a public Space is a token you have given away","url":"https://addaly.com/learn/hugging-face/tokens-and-gates"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'Shipping the thing you trained', in Hugging Face, End to End. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/hugging-face/publishing-an-adapter (Accessed: 13 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"Shipping the thing you trained","courseTitle":"Hugging Face, End to End","module":{"index":7,"slug":"training-with-the-stack","title":"Training and adapting, with the libraries the Hub is built on"},"answer":null,"keyIdea":"Publish the adapter rather than a merged republication of somebody else’s weights, record the base model, training configuration and a measured result against a baseline in the card, and keep the dataset and evaluation set, because the adapter expires when its base is superseded and the data does not.","hero":{"kind":"plate","image":null,"alt":null,"caption":null},"blocks":[{"t":"h2","id":"push-the-adapter-not-the-merged-model","runs":[{"text":"Push the adapter, not the merged model"}]},{"t":"code","lang":"python","label":"Python","text":"model.push_to_hub(\"your-name/support-hi-lora\")\ntok.push_to_hub(\"your-name/support-hi-lora\")"},{"t":"p","runs":[{"text":"A LoRA adapter is a few megabytes. The merged model is several gigabytes of mostly somebody else’s weights, republished. Publish the adapter:"}]},{"t":"ul","items":[[{"text":"It is honest about what you made."}],[{"text":"It is fast to download and cheap to store."}],[{"text":"It stays usable when the base is updated, and it makes clear which base it needs."}],[{"text":"Several adapters can share one loaded base at serving time."}]]},{"t":"p","runs":[{"text":"Publish a merged version as well only when there is a reason: a serving stack that cannot load adapters, or a GGUF conversion for people running on CPUs. Say in the card that it is a merge of your adapter and name the base."}]},{"t":"h2","id":"the-adapter-card","runs":[{"text":"The adapter card"}]},{"t":"p","runs":[{"text":"adapter_config.json","code":true},{"text":" records "},{"text":"base_model_name_or_path","code":true},{"text":", and the Hub uses it to link your repository to its parent. Add the metadata that makes it findable and usable:"}]},{"t":"code","lang":"yaml","label":"YAML","text":"---\nbase_model: Qwen/Qwen2.5-7B-Instruct\nlibrary_name: peft\ntags:\n  - lora\n  - sft\nlanguage:\n  - hi\n  - en\nlicense: apache-2.0\ndatasets:\n  - your-name/support-conversations\n---"},{"t":"p","runs":[{"text":"The "},{"text":"license","code":true},{"text":" field needs a moment’s thought and the next module covers it properly. The short version: "},{"text":"your adapter inherits obligations from the base model’s licence.","b":true},{"text":" A LoRA trained on a model with a restrictive licence does not become permissive because you wrote Apache 2.0 in your own front matter. State the base’s terms as well as your own."}]},{"t":"h2","id":"what-the-prose-must-contain","runs":[{"text":"What the prose must contain"}]},{"t":"p","runs":[{"text":"Six things, and a card without them is not usable by a stranger:"}]},{"t":"ol","start":1,"items":[[{"text":"A runnable snippet","b":true},{"text":", with the base model named and the revision pinned:"}]]},{"t":"code","lang":"python","label":"Python","text":"from peft import PeftModel\nfrom transformers import AutoModelForCausalLM, AutoTokenizer\n\nbase = AutoModelForCausalLM.from_pretrained(\"Qwen/Qwen2.5-7B-Instruct\",\n                                            dtype=\"bfloat16\", device_map=\"auto\")\nmodel = PeftModel.from_pretrained(base, \"your-name/support-hi-lora\")\ntok = AutoTokenizer.from_pretrained(\"your-name/support-hi-lora\")"},{"t":"ol","start":2,"items":[[{"text":"What it was trained on","b":true},{"text":": how many examples, from where, in what languages, and the dataset repository if you can publish it."}],[{"text":"The training configuration","b":true},{"text":": rank, alpha, target modules, learning rate, epochs, base precision. Six lines, and without them nobody can reproduce or extend your work."}],[{"text":"A measured result.","b":true},{"text":" Even “macro-F1 0.78 on 300 held-out tickets, against 0.61 for the base model” is infinitely better than “works well”. Say what the baseline was."}],[{"text":"A named limitation.","b":true},{"text":" The thing it does badly, from your own testing. Every model has one and a card without one reads as untested."}],[{"text":"Whether the training data contained personal information","b":true},{"text":", and what you did about it."}]]},{"t":"h2","id":"before-you-push","runs":[{"text":"Before you push"}]},{"t":"ul","items":[[{"text":"Check for secrets in the output directory.","b":true},{"text":" A "},{"text":".env","code":true},{"text":", a notebook with a token in a cell, a config with a connection string. "},{"text":"git status","code":true},{"text":" in "},{"text":"output_dir","code":true},{"text":" before uploading, and use "},{"text":"ignore_patterns","code":true},{"text":" on "},{"text":"upload_folder","code":true},{"text":"."}],[{"text":"Check for training data you cannot publish.","b":true},{"text":" Checkpoint directories sometimes contain a copy of the tokenised dataset."}],[{"text":"Start private.","b":true},{"text":" Make it public when you have read your own card back."}]]},{"t":"h2","id":"serving-several-adapters-on-one-base","runs":[{"text":"Serving several adapters on one base"}]},{"t":"code","lang":"python","label":"Python","text":"model.load_adapter(\"your-name/support-hi-lora\", adapter_name=\"hi\")\nmodel.load_adapter(\"your-name/support-ta-lora\", adapter_name=\"ta\")\nmodel.set_adapter(\"hi\")"},{"t":"p","runs":[{"text":"One base in memory, several behaviours, switched per request. For a product serving several languages or several customers, this is the architecture that makes fine-tuning affordable: one 14 GB base and a directory of 20 MB adapters, rather than one 14 GB model per variant."}]},{"t":"h2","id":"the-maintenance-you-are-signing-up-for","runs":[{"text":"The maintenance you are signing up for"}]},{"t":"p","runs":[{"text":"An adapter is pinned to its base. When a better base arrives in a few months, your adapter does not transfer: the weight shapes may match and the behaviour will not, because the thing it learned to correct has changed. You retrain."}]},{"t":"p","runs":[{"text":"This is why the dataset matters more than the adapter. Keep the training data, the configuration and the evaluation set in a repository, and retraining against a new base is an afternoon. Keep only the adapter and you are starting again."}]},{"t":"h2","id":"do-this-now","runs":[{"text":"Do this now"}],"role":"try"},{"t":"p","runs":[{"text":"Publish an adapter privately, then open it in a fresh Colab session and load it from the card’s snippet alone. If it does not work, your card is wrong, and that is exactly the test every model card should have to pass before it goes public."}]}],"check":{"question":[{"text":"Why is publishing a LoRA adapter generally preferable to publishing the merged full model?"}],"options":[{"text":[{"text":"Merged models lose the improvement, because merging approximates the adapter’s effect rather than reproducing it."}],"correct":false,"tempting":[{"text":"Merging is exact arithmetic for the trained precision; the approximation concern applies narrowly to QLoRA."}]},{"text":[{"text":"Adapters can be applied to any base model of the same size, which merged weights cannot."}],"correct":false,"tempting":[{"text":"Adapters are tied to their exact base; shape compatibility is not behavioural compatibility."}]},{"text":[{"text":"It is a few megabytes, states which base it needs, and lets one loaded base serve several adapters."}],"correct":true,"tempting":null},{"text":[{"text":"The Hub charges for storage above a threshold, so large merged repositories incur costs adapters avoid."}],"correct":false,"tempting":[{"text":"Public repository storage is not the constraint that drives this choice."}]}],"order":[3,0,2,1],"why":null},"prev":{"slug":"what-goes-wrong-in-training","title":"Loss is NaN, memory is gone, nothing is learning","minutes":9},"next":{"slug":"tokens-and-gates","title":"A token in a public Space is a token you have given away","minutes":8}}}