Symmetry: save_pretrained / from_pretrained
Every PreTrainedModel, PreTrainedTokenizer, and processor in the library has the same pair: save_pretrained(path) writes config + weights + tokenizer files to a directory, and from_pretrained(path or repo_id) loads them. Local paths and Hub repo ids are interchangeable; the loader sniffs which one you passed.
This symmetry is the reason the entire ecosystem composes. A LoRA trainer outputs a directory; you pass that directory to AutoPeftModelForCausalLM; you pass the merged directory to push_to_hub(); the next person calls from_pretrained on your repo id. End-to-end: same two methods.
push_to_hub: the underrated half
Both model.push_to_hub("yourname/your-repo") and tokenizer.push_to_hub(...) create or update a Hub repo. The first call creates with a default README; subsequent calls update. create_repo() from huggingface_hub gives you the explicit control (private, exist_ok, organization, etc.).
For weight uploads, prefer the safetensors format (default since transformers 4.34). It's safer (no pickle), faster (memory-mapped), and smaller (no Python overhead).