{"course":{"slug":"hugging-face","title":"Hugging Face, End to End","url":"https://addaly.com/learn/hugging-face","level":"intermediate","track":"create"},"lesson":{"slug":"inside-a-repository","title":"What the files in a model repository are for","url":"https://addaly.com/learn/hugging-face/inside-a-repository","position":5,"of":75,"minutes":10,"answer":"Five files answer the questions a model card often does not: config.json gives the architecture and context length, the tokenizer files carry the chat template, the safetensors index gives the true download size.","keyIdea":"Five files answer the questions a model card often does not: config.json gives the architecture and context length, the tokenizer files carry the chat template, the safetensors index gives the true download size, generation_config sets sampling defaults you did not choose, and an adapter_config means you are looking at a LoRA rather than a model."},"body":{"format":"markdown","text":"## Open Files and versions before you open anything else\n\nA typical transformers model repository contains eight to fifteen files. Five of them decide whether the model will work for you, and you can read all five in a browser in under a minute: no download, no GPU, no install. This is the cheapest diagnostic on the Hub and almost nobody uses it.\n\nHere is what you are looking at.\n\n## `config.json`: the model's shape\n\nThis is the architecture, in JSON. A trimmed example:\n\n> Figure: Five files, and the question each one answers. Layers, top to bottom: config.json (Architecture, context length, saved dtype, vocabulary size); tokenizer_config.json (The chat template, the padding side, the special tokens); model.safetensors.index.json (total_size: the honest disk bill, before duplicate formats); generation_config.json (The temperature and do_sample you did not choose); adapter_config.json (Not a model: a LoRA, and the exact base it needs). All five are readable in a browser in under a minute, with no download, no GPU and no install. Four of them answer questions the prose card usually does not, and the fifth tells you that what you are looking at is not a model.\n\n```json\n{\n  \"architectures\": [\"LlamaForCausalLM\"],\n  \"hidden_size\": 4096,\n  \"num_hidden_layers\": 32,\n  \"num_attention_heads\": 32,\n  \"max_position_embeddings\": 131072,\n  \"vocab_size\": 128256,\n  \"torch_dtype\": \"bfloat16\",\n  \"rope_theta\": 500000.0\n}\n```\n\nFour fields earn your attention:\n\n- **`architectures`** names the Python class that will be instantiated. `LlamaForCausalLM` tells you this is a text generator; `BertForSequenceClassification` tells you it is a classifier with a fixed number of labels. If this names a class your installed transformers version does not have, you get `KeyError` or a message about an unrecognised model type, and the fix is upgrading transformers rather than anything to do with the model.\n- **`max_position_embeddings`** is the context length. This is the number the model name never tells you and the one that most often decides a project.\n- **`torch_dtype`** records what precision the weights were saved in. It is a record, not an instruction: loading does not automatically honour it in older versions of transformers, which is how people end up holding a bfloat16 model in float32 and using twice the memory they budgeted.\n- **`vocab_size`** must match the tokenizer. When it does not (usually after somebody added special tokens and did not resize the embedding matrix) you get an index error deep inside the forward pass, or silent nonsense.\n\nYou can read it without a browser:\n\n```bash\ncurl -s https://huggingface.co/meta-llama/Llama-3.2-1B/resolve/main/config.json | head -40\n```\n\n## The tokenizer files: three of them, doing different jobs\n\n- **`tokenizer.json`** is the fast tokenizer: the full vocabulary and merge rules in one file, loaded by the Rust implementation. Its presence is why `AutoTokenizer` is fast.\n- **`tokenizer_config.json`** holds the settings around it: padding side, maximum length, the special token names, and, on chat models, the **`chat_template`**. That template is a Jinja string, and it is the difference between a chat model working and a chat model producing drivel. It is worth reading.\n- **`special_tokens_map.json`** maps roles like `bos_token`, `eos_token`, `pad_token` onto actual strings.\n\nOlder repositories carry `vocab.txt`, `merges.txt` or `spiece.model` instead. They work; they load through the slow Python path unless a converter runs.\n\n## The weights, and why there are several files\n\n`model.safetensors` is the whole thing when the model is small. Above a few gigabytes it is sharded:\n\n```\nmodel-00001-of-00004.safetensors\nmodel-00002-of-00004.safetensors\nmodel-00003-of-00004.safetensors\nmodel-00004-of-00004.safetensors\nmodel.safetensors.index.json\n```\n\nThe index is a map from every parameter name to the shard that holds it, plus a total byte count. It is small, it is readable, and the `total_size` field in it is the fastest honest answer to \"how much disk will this cost me\", more reliable than adding up what the file listing shows, because the listing also contains formats you will not download.\n\nMany repositories carry **both** `.safetensors` and `.bin` copies of the same weights. Downloading the repository naively fetches both and doubles your bill. The next lesson explains why you want the safetensors ones specifically.\n\n## `generation_config.json`: the defaults you did not choose\n\nFor generative models this file sets `temperature`, `top_p`, `do_sample`, `max_new_tokens`, `eos_token_id` and friends. When people compare two models and one seems wilder, the cause is frequently that their published generation defaults differ, not that the model does. If you are benchmarking, set these explicitly rather than inheriting them.\n\n## `.gitattributes`, and files with no purpose\n\n`.gitattributes` tells git which patterns go to large-file storage. You will never edit it by hand and you should never delete it. Beyond that you will find `README.md`, which is the model card, plus example images, an `onnx/` folder, and sometimes a `runs/` directory full of training logs somebody forgot to exclude. That last one is occasionally the most informative thing in the repository.\n\n## A repository can also be an adapter\n\nIf you see `adapter_config.json` and an `adapter_model.safetensors` of a few dozen megabytes, this is not a model. It is a LoRA adapter: a small set of extra matrices that must be applied on top of the base model named inside `adapter_config.json`. Downloading it alone and wondering why nothing loads is a rite of passage.\n\n## Do this now\n\nOpen any chat model's `tokenizer_config.json` and find the `chat_template`. Read the Jinja. You are looking at the exact strings the model was trained to expect around a user turn; and once you have seen them, the whole category of \"the model ignores my system prompt\" bugs becomes legible."},"check":{"question":"A repository lists ten files totalling 46 GB, but its `model.safetensors.index.json` reports a `total_size` of about 16 GB. What is the most likely explanation?","options":[{"text":"The index records the size after compression, and the listing shows the uncompressed files that will land on disk.","correct":false,"tempting":"Assumes safetensors files are compressed; they store raw tensor bytes with a JSON header and no compression."},{"text":"The repository holds the same weights in two formats, and the index counts only the safetensors shards.","correct":true},{"text":"The index is stale because it was written before the final training shards were uploaded.","correct":false,"tempting":"Treats a routine duplicate-format layout as corruption, when a stale index would cause load failures rather than a size discrepancy."},{"text":"The extra 30 GB is the optimiser state, which the index deliberately excludes because it is not needed for inference.","correct":false,"tempting":"Plausible-sounding but rare in published inference repositories, and optimiser state would be named as such rather than as model weights."}]},"prev":{"slug":"reading-a-model-name","title":"A model's name is a specification, if you can read it","url":"https://addaly.com/learn/hugging-face/reading-a-model-name"},"next":{"slug":"safetensors-and-pickle","title":"Loading a model can run someone else's code","url":"https://addaly.com/learn/hugging-face/safetensors-and-pickle"},"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) 'What the files in a model repository are for', in Hugging Face, End to End. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/hugging-face/inside-a-repository (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":"What the files in a model repository are for","courseTitle":"Hugging Face, End to End","module":{"index":1,"slug":"the-hub-as-a-repository-host","title":"The Hub, read properly"},"answer":null,"keyIdea":"Five files answer the questions a model card often does not: config.json gives the architecture and context length, the tokenizer files carry the chat template, the safetensors index gives the true download size, generation_config sets sampling defaults you did not choose, and an adapter_config means you are looking at a LoRA rather than a model.","hero":{"kind":"figure","figure":{"t":"figure","label":"Figure","figure":{"type":"stack","title":"Five files, and the question each one answers","layers":[{"label":"config.json","detail":"Architecture, context length, saved dtype, vocabulary size"},{"label":"tokenizer_config.json","detail":"The chat template, the padding side, the special tokens"},{"label":"model.safetensors.index.json","detail":"total_size: the honest disk bill, before duplicate formats"},{"label":"generation_config.json","detail":"The temperature and do_sample you did not choose"},{"label":"adapter_config.json","detail":"Not a model: a LoRA, and the exact base it needs"}],"caption":"All five are readable in a browser in under a minute, with no download, no GPU and no install. Four of them answer questions the prose card usually does not, and the fifth tells you that what you are looking at is not a model."},"alt":"Figure: Five files, and the question each one answers. Layers, top to bottom: config.json (Architecture, context length, saved dtype, vocabulary size); tokenizer_config.json (The chat template, the padding side, the special tokens); model.safetensors.index.json (total_size: the honest disk bill, before duplicate formats); generation_config.json (The temperature and do_sample you did not choose); adapter_config.json (Not a model: a LoRA, and the exact base it needs). All five are readable in a browser in under a minute, with no download, no GPU and no install. Four of them answer questions the prose card usually does not, and the fifth tells you that what you are looking at is not a model."}},"blocks":[{"t":"h2","id":"open-files-and-versions-before-you-open-anything-else","runs":[{"text":"Open Files and versions before you open anything else"}]},{"t":"p","runs":[{"text":"A typical transformers model repository contains eight to fifteen files. Five of them decide whether the model will work for you, and you can read all five in a browser in under a minute: no download, no GPU, no install. This is the cheapest diagnostic on the Hub and almost nobody uses it."}]},{"t":"p","runs":[{"text":"Here is what you are looking at."}]},{"t":"h2","id":"config-json-the-model-s-shape","runs":[{"text":"config.json","code":true},{"text":": the model’s shape"}]},{"t":"p","runs":[{"text":"This is the architecture, in JSON. A trimmed example:"}]},{"t":"code","lang":"json","label":"JSON","text":"{\n  \"architectures\": [\"LlamaForCausalLM\"],\n  \"hidden_size\": 4096,\n  \"num_hidden_layers\": 32,\n  \"num_attention_heads\": 32,\n  \"max_position_embeddings\": 131072,\n  \"vocab_size\": 128256,\n  \"torch_dtype\": \"bfloat16\",\n  \"rope_theta\": 500000.0\n}"},{"t":"p","runs":[{"text":"Four fields earn your attention:"}]},{"t":"ul","items":[[{"text":"architectures","b":true,"code":true},{"text":" names the Python class that will be instantiated. "},{"text":"LlamaForCausalLM","code":true},{"text":" tells you this is a text generator; "},{"text":"BertForSequenceClassification","code":true},{"text":" tells you it is a classifier with a fixed number of labels. If this names a class your installed transformers version does not have, you get "},{"text":"KeyError","code":true},{"text":" or a message about an unrecognised model type, and the fix is upgrading transformers rather than anything to do with the model."}],[{"text":"max_position_embeddings","b":true,"code":true},{"text":" is the context length. This is the number the model name never tells you and the one that most often decides a project."}],[{"text":"torch_dtype","b":true,"code":true},{"text":" records what precision the weights were saved in. It is a record, not an instruction: loading does not automatically honour it in older versions of transformers, which is how people end up holding a bfloat16 model in float32 and using twice the memory they budgeted."}],[{"text":"vocab_size","b":true,"code":true},{"text":" must match the tokenizer. When it does not (usually after somebody added special tokens and did not resize the embedding matrix) you get an index error deep inside the forward pass, or silent nonsense."}]]},{"t":"p","runs":[{"text":"You can read it without a browser:"}]},{"t":"code","lang":"bash","label":"Bash","text":"curl -s https://huggingface.co/meta-llama/Llama-3.2-1B/resolve/main/config.json | head -40"},{"t":"h2","id":"the-tokenizer-files-three-of-them-doing-different-jobs","runs":[{"text":"The tokenizer files: three of them, doing different jobs"}]},{"t":"ul","items":[[{"text":"tokenizer.json","b":true,"code":true},{"text":" is the fast tokenizer: the full vocabulary and merge rules in one file, loaded by the Rust implementation. Its presence is why "},{"text":"AutoTokenizer","code":true},{"text":" is fast."}],[{"text":"tokenizer_config.json","b":true,"code":true},{"text":" holds the settings around it: padding side, maximum length, the special token names, and, on chat models, the "},{"text":"chat_template","b":true,"code":true},{"text":". That template is a Jinja string, and it is the difference between a chat model working and a chat model producing drivel. It is worth reading."}],[{"text":"special_tokens_map.json","b":true,"code":true},{"text":" maps roles like "},{"text":"bos_token","code":true},{"text":", "},{"text":"eos_token","code":true},{"text":", "},{"text":"pad_token","code":true},{"text":" onto actual strings."}]]},{"t":"p","runs":[{"text":"Older repositories carry "},{"text":"vocab.txt","code":true},{"text":", "},{"text":"merges.txt","code":true},{"text":" or "},{"text":"spiece.model","code":true},{"text":" instead. They work; they load through the slow Python path unless a converter runs."}]},{"t":"h2","id":"the-weights-and-why-there-are-several-files","runs":[{"text":"The weights, and why there are several files"}]},{"t":"p","runs":[{"text":"model.safetensors","code":true},{"text":" is the whole thing when the model is small. Above a few gigabytes it is sharded:"}]},{"t":"code","lang":"","label":"Text","text":"model-00001-of-00004.safetensors\nmodel-00002-of-00004.safetensors\nmodel-00003-of-00004.safetensors\nmodel-00004-of-00004.safetensors\nmodel.safetensors.index.json"},{"t":"p","runs":[{"text":"The index is a map from every parameter name to the shard that holds it, plus a total byte count. It is small, it is readable, and the "},{"text":"total_size","code":true},{"text":" field in it is the fastest honest answer to “how much disk will this cost me”, more reliable than adding up what the file listing shows, because the listing also contains formats you will not download."}]},{"t":"p","runs":[{"text":"Many repositories carry "},{"text":"both","b":true},{"text":" "},{"text":".safetensors","code":true},{"text":" and "},{"text":".bin","code":true},{"text":" copies of the same weights. Downloading the repository naively fetches both and doubles your bill. The next lesson explains why you want the safetensors ones specifically."}]},{"t":"h2","id":"generationconfig-json-the-defaults-you-did-not-choose","runs":[{"text":"generation_config.json","code":true},{"text":": the defaults you did not choose"}]},{"t":"p","runs":[{"text":"For generative models this file sets "},{"text":"temperature","code":true},{"text":", "},{"text":"top_p","code":true},{"text":", "},{"text":"do_sample","code":true},{"text":", "},{"text":"max_new_tokens","code":true},{"text":", "},{"text":"eos_token_id","code":true},{"text":" and friends. When people compare two models and one seems wilder, the cause is frequently that their published generation defaults differ, not that the model does. If you are benchmarking, set these explicitly rather than inheriting them."}]},{"t":"h2","id":"gitattributes-and-files-with-no-purpose","runs":[{"text":".gitattributes","code":true},{"text":", and files with no purpose"}]},{"t":"p","runs":[{"text":".gitattributes","code":true},{"text":" tells git which patterns go to large-file storage. You will never edit it by hand and you should never delete it. Beyond that you will find "},{"text":"README.md","code":true},{"text":", which is the model card, plus example images, an "},{"text":"onnx/","code":true},{"text":" folder, and sometimes a "},{"text":"runs/","code":true},{"text":" directory full of training logs somebody forgot to exclude. That last one is occasionally the most informative thing in the repository."}]},{"t":"h2","id":"a-repository-can-also-be-an-adapter","runs":[{"text":"A repository can also be an adapter"}]},{"t":"p","runs":[{"text":"If you see "},{"text":"adapter_config.json","code":true},{"text":" and an "},{"text":"adapter_model.safetensors","code":true},{"text":" of a few dozen megabytes, this is not a model. It is a LoRA adapter: a small set of extra matrices that must be applied on top of the base model named inside "},{"text":"adapter_config.json","code":true},{"text":". Downloading it alone and wondering why nothing loads is a rite of passage."}]},{"t":"h2","id":"do-this-now","runs":[{"text":"Do this now"}],"role":"try"},{"t":"p","runs":[{"text":"Open any chat model’s "},{"text":"tokenizer_config.json","code":true},{"text":" and find the "},{"text":"chat_template","code":true},{"text":". Read the Jinja. You are looking at the exact strings the model was trained to expect around a user turn; and once you have seen them, the whole category of “the model ignores my system prompt” bugs becomes legible."}]}],"check":{"question":[{"text":"A repository lists ten files totalling 46 GB, but its "},{"text":"model.safetensors.index.json","code":true},{"text":" reports a "},{"text":"total_size","code":true},{"text":" of about 16 GB. What is the most likely explanation?"}],"options":[{"text":[{"text":"The index records the size after compression, and the listing shows the uncompressed files that will land on disk."}],"correct":false,"tempting":[{"text":"Assumes safetensors files are compressed; they store raw tensor bytes with a JSON header and no compression."}]},{"text":[{"text":"The repository holds the same weights in two formats, and the index counts only the safetensors shards."}],"correct":true,"tempting":null},{"text":[{"text":"The index is stale because it was written before the final training shards were uploaded."}],"correct":false,"tempting":[{"text":"Treats a routine duplicate-format layout as corruption, when a stale index would cause load failures rather than a size discrepancy."}]},{"text":[{"text":"The extra 30 GB is the optimiser state, which the index deliberately excludes because it is not needed for inference."}],"correct":false,"tempting":[{"text":"Plausible-sounding but rare in published inference repositories, and optimiser state would be named as such rather than as model weights."}]}],"order":[3,0,2,1],"why":null},"prev":{"slug":"reading-a-model-name","title":"A model’s name is a specification, if you can read it","minutes":8},"next":{"slug":"safetensors-and-pickle","title":"Loading a model can run someone else’s code","minutes":9}}}