Make things · Intermediate
Hugging Face, End to End
Draft, open for academic review
Read a model card licence-first, run a Space for free, keep a token safe, publish something of your own.
Your progress here is recorded once you pass the Image Generation, in Practice exam.

8 modules. 75 lessons.
The Hub, read properly
9 lessons · 1 h 22
You will be able toFind a specific model on the Hub using filters and the Python API rather than the search box, read its repository files to say what architecture it is, what format its weights are in and what code will run when you load it, and explain what the download counter is actually counting.
- 1.1The Hub is git, and that explains everything else
- 1.2The search box is the worst way in
- 1.3Read the licence before you read the demo
- 1.4A model’s name is a specification, if you can read it
- 1.5What the files in a model repository are for
- 1.6Loading a model can run someone else’s code
- 1.7Four shelves, and picking the wrong one costs a weekend
- 1.8Ask the Hub questions instead of browsing it
- 1.9What a download count is counting
An illustrative case, built from documented patternsThe name that was spelled two ways
Since January the clinic had been running one script. It took an audio file from a hearing, pushed it through a large open speech model, and wrote a transcript into the matter’s folder. Fourteen hundred hours of audio had gone through it by August. The script was ninety lines long and nobody had touched it since the week it was written.
Nobody in the clinic thought of it as software. It was the thing that turned nine hours of a bad recording made on a phone at the back of a courtroom into text a lawyer could read on a train. The volunteers called it the transcriber, and the only thing anybody ever changed about it was the folder it wrote to.
In August a paralegal called Sushma noticed that a respondent’s surname appeared as Waghmare in one transcript and Vaghmare in another, in the same matter, from two hearings six weeks apart. She assumed a typing error until she found three more, all in files produced after June.
Anup, the volunteer who had written the script, opened the model’s Files and versions tab and then its History. There were two commits since January. One in April replaced the tokenizer files. One in June reuploaded a corrected checkpoint, with a message saying it fixed a bug affecting proper nouns.
The script said from_pretrained("openai/whisper-large-v3"). No revision. So it had never meant one model. It had meant whatever was on the main branch at the moment each transcript was made, and the archive was not one archive but three, with nothing in any filename to say which.
The decision
The clinic quotes these transcripts in written submissions. Two options were on the table and both cost something real.
Re-transcribe everything. The donated allocation was twenty hours a week on a college lab machine. Fourteen hundred hours of audio at roughly real time is about three weeks of that allocation, during which no new intake could be transcribed. Intake does not pause. Three weeks meant roughly ninety new matters waiting on a transcript before anybody could read them.
Or pin from today, accept an archive produced by three different models, and carry the risk. That risk is not a daily cost. It is a credibility cost that arrives once, unpredictably, in front of a bench, when opposing counsel asks why two documents from the same clinic spell the same person’s name differently.
Anup made one more check before anybody chose. The June commit message said the fix concerned proper nouns. That narrowed the problem from everywhere to one place, and one place happened to be what a legal transcript is mostly about. He also asked a question nobody had asked yet: how much of the archive belongs to matters still before a court. The answer was a hundred and ninety hours. The rest was closed, or kept for research.
The cost on one side was ninety delayed matters. The cost on the other was a document nobody could vouch for. The information that made the choice tractable was already sitting in the repository, free, in a commit message and a list of dates.
The clinic also had to decide what it was willing to say out loud. A transcript in a submission is offered as a record of what was said. Saying that some of the clinic’s transcripts were produced by a model that has since been corrected is an uncomfortable sentence, and it is a sentence somebody would eventually have to say either way: the only question was whether the clinic said it first, in a note it had written calmly, or second, in an answer to a question from a bench.
Anup pointed out one more thing that made the pinning decision easy even if the re-running decision was not. Pinning costs nothing. It is one extra argument in three function calls, it can be done in ten minutes, and it does not compete with anything for the donated GPU hours. Whatever they chose about the existing fourteen hundred hours, there was no version of the argument in which they should keep loading an unpinned model tomorrow.
How it played out
They pinned. Every loading call in the script got a revision argument carrying the June commit hash, copied out of the History tab, including the one that loads the processor. They re-ran the hundred and ninety hours belonging to open matters, which took four days rather than three weeks, and left the closed archive as it was with a one-page note in the shared drive recording which date ranges came from which revision and what the June fix had changed.
Every transcript produced from then on carried a header line with the model identifier, the revision hash and the date it was made. Six months later a bench did ask about a spelling. The clinic answered in one sentence and produced the note. Writing the note had cost an afternoon.
Worth arguing about
Anup’s script had not been edited since January, yet the transcripts it produced changed. Explain the mechanism.
One answer
A Hugging Face repository is a git repository and
from_pretrained("owner/name")without arevisionargument resolves to whatever is on the main branch at the moment the code runs. Two commits landed between January and August, so the same ninety lines of code loaded three different sets of weights and tokenizer files over the period. Pinningrevisionto a commit hash removes the whole category, and costs nothing.Reading the June commit message changed the size of the problem. Why is that, and what does it say about the History tab?
One answer
The message said the fix affected proper nouns. That turned an unbounded worry (every word of fourteen hundred hours might differ) into a bounded one concentrated in names. History is a diff and not merely a list of dates, so it answers what changed as well as when. For a model that started behaving differently last week, it is the first place to look and often the last.
They re-ran a hundred and ninety hours rather than fourteen hundred. What makes that a defensible decision rather than a convenient shortcut?
One answer
They split the archive along the axis where the risk actually sits. A transcript from a closed matter is not going to be read back to a judge; one from an open matter might be. They also recorded what they had not re-run and why, which is the part that turns a scoped decision into an auditable one. A shortcut hides the gap; this documented it.
Test yourself6 questions on this module
A transcription script has not been edited since January, yet the transcripts it produced in July differ from those it produced in March. What is the mechanism?
You clone a model repository with plain git and find that model.safetensors is about 130 bytes. What has happened?
A model shows four million downloads over thirty days. What is the strongest inference you can draw from that number?
Your laptop cannot run a model at full precision. What is the fastest route to a version that fits?
A card tags a fine-tune apache-2.0. Its base_model field names a repository under a non-commercial research licence. Which terms govern your paid product?
A repository ships only safetensors weights, and its usage snippet passes trust_remote_code=True. What risk remains?
Transformers, from characters to output
9 lessons · 1 h 21
You will be able toTrace a piece of text from characters through a tokenizer to logits and back to text with the transformers library, and say which of the Auto class, the tokenizer, the padding side, the chat template or the generation config is at fault when a model loads without error and produces nonsense.
- 2.1One line that hides five steps
- 2.2AutoModel gives you numbers, not answers
- 2.3Your text is integers, and the count is not the word count
- 2.4Padding on the wrong side ruins generation quietly
- 2.5Logits, and the four knobs between them and words
- 2.6The chat template is not optional
- 2.7Throughput and latency are different problems
- 2.8Showing tokens as they arrive, and making them stop
- 2.9It loaded, it ran, the output is nonsense
An illustrative case, built from documented patternsThe classifier that was ninety-nine per cent sure of nothing
The forms came back from two hundred and twelve schools, most of them handwritten and then typed up by clerks. About two thirds were in Marathi and the rest in English or a mixture. The review meeting that would allocate a maintenance budget wanted a picture of what parents were complaining about, and nobody had ever put numbers on the forms before.
The clerks had done their part properly. Every form had a school code, a date and a line of free text that ran anywhere from four words to nine hundred. What nobody had was a way of saying how many of those lines were about a leaking roof and how many were about a teacher who does not come.
Rohit, on a three-month placement, had the pipeline working in an afternoon. Six lines, a well-known model name he had seen in a tutorial, and a loop over a spreadsheet. The output looked entirely believable: sixty-one per cent of forms in one category, the rest spread across three others, with confidence scores mostly above 0.95.
The labels came back as LABEL_0 and LABEL_1. He mapped LABEL_1 to positive because one usually means yes, and moved on.
The decision
Shalini Pawar, the deputy director, had a pack due in four days and this was the only quantitative thing in it. The alternative to using it was two days of people reading forms by hand, and there were no two days.
She took sixty forms home instead and labelled them herself over one evening. The model agreed with her on thirty-three. That is a coin flip with a very good opinion of itself.
Rohit went back to the output of his first run and found the warning he had scrolled past. It said that two tensors, the classifier weight and the classifier bias, had not been found in the checkpoint and had been newly initialised, and that he should probably train the model before using it for predictions. He had loaded a base checkpoint with no classification head. The library had filled the head with random numbers, warned in a log line, and then run perfectly.
Two more things surfaced the same evening. The model was English-only, and two thirds of the forms were in Marathi. And on that model’s vocabulary a Marathi sentence cost three to four times as many tokens as its English equivalent, so the five-hundred-and-twelve-token truncation was cutting long complaints in half, and a complaint puts its polite introduction first and its accusation last.
So the choice in front of Shalini was not whether to trust a slightly noisy number. It was whether to present a fabricated distribution to a meeting that allocates money, or to arrive with less than she had promised.
Shalini’s evening of sixty forms is worth dwelling on, because it is the cheapest thing in this case study and it is the only reason anything was caught. She did not audit the code, read the model card, or ask anybody technical. She took the output and the input and compared them by hand, on a sample small enough to finish, and the answer was unambiguous within an hour.
Rohit’s instinct afterwards was to blame the model, which is the tenth item on the checklist rather than the first. The model was fine. It was a perfectly good base checkpoint, downloaded four million times, doing exactly what a base checkpoint does. What had gone wrong was a mismatch between the class he asked for and the weights that existed, and a warning that described the mismatch in plain English in the terminal, above the first result.
How it played out
She withdrew the automatic pass. Four people hand-labelled four hundred forms over two days, chosen to cover all two hundred and twelve schools rather than the first four hundred in the pile, and the pack reported those four hundred with the disagreement rate between labellers printed beside every figure. One paragraph explained that an automatic pass had been attempted and withdrawn, and why. The review accepted it, and the disagreement rate turned out to be the number the committee argued about most usefully.
Rohit rebuilt the pipeline on a multilingual model whose card named its labels in id2label, set the truncation side to the left so the end of a complaint survived, and added five lines at the top of the script that refuse to run if id2label still reads LABEL_0 or if loading raised an initialisation warning. On the four hundred hand-labelled forms the new pipeline agreed three hundred and forty-one times: a number that could be quoted, because the sample it came from could be named.
Worth arguing about
Why did the model return confident scores instead of failing, and what did the 0.95 confidence actually measure?
One answer
The checkpoint was a base model with no classification head, so transformers created one, filled it with random numbers and emitted a warning rather than an exception, deliberately, because that is the normal first step before fine-tuning. The score is a softmax over the head’s raw outputs. A softmax over random numbers is still a distribution and still has a maximum, so it always looks confident. It is not a probability of being right.
Two thirds of the forms were in Marathi on an English-centric tokenizer. Name two separate costs that follows from, beyond accuracy.
One answer
First, the token count: the same meaning costs three to four times as many tokens, so a fixed truncation limit holds far less Marathi than English and the input is silently cut. Second, where the cut falls: truncation defaults to the right, and a complaint’s substance is usually at the end, so the classifier read the polite opening and never reached the accusation. Setting
truncation_sideto left, or chunking, fixes the second; only a better vocabulary fixes the first.Rohit’s five new lines refuse to run under two conditions. Why are those two worth an automatic check rather than a habit?
One answer
Both failures are silent and both survive every downstream test. A randomly initialised head produces output of exactly the right shape, and LABEL_0 output looks like a label until somebody asks which class it is. Neither raises, so neither can be caught by watching for errors; they can only be caught by asserting the thing you assumed. A habit is forgotten under a deadline and an assertion is not.
Test yourself6 questions on this module
A default sentiment pipeline returns a score of 0.9993 on a Hinglish sentence. What does that number actually measure?
Loading a base checkpoint with AutoModelForSequenceClassification warns that classifier.weight was newly initialised. What will the model then do?
The same twelve-word sentence costs 15 tokens in English and 47 in Hindi on one model. What causes the difference?
A prompt that is answered well on its own returns generic text inside a batch of eight. What is the first suspect?
You pass temperature=0.9 and top_p=0.95 to generate, and every run returns an identical answer. What is the likely cause?
A fine-tuned chat model rambles past its answer and invents a new user turn. Which explanation fits best?
Running it on the hardware you actually have
9 lessons · 1 h 22
You will be able toCompute from a model’s parameter count and dtype whether it will fit the machine in front of you, pick between a full-precision, quantized, GGUF or ONNX route for a stated constraint, and measure peak memory, first-token latency and quality loss well enough to defend the choice.
- 3.1Your disk fills up before your patience does
- 3.2The memory arithmetic you can do in your head
- 3.3Spreading a model across whatever you have
- 3.4Quantization: what you buy and what you pay
- 3.5GGUF, and the laptop that has no GPU
- 3.6ONNX, Optimum, and models that run in a browser tab
- 3.7Where the free GPUs actually are
- 3.8What actually runs on eight gigabytes
- 3.9Proving the change helped
An illustrative case, built from documented patternsEleven old machines against one new one
The doubt register was a paper book on the lab desk. A student wrote a question, a teacher answered it the next morning, and by then the student had moved on. Meena Joshi, who ran the centre, wanted something that answered at the moment the doubt existed.
The eleven machines were bought in 2019 for a computer-literacy course that no longer ran. Eight gigabytes each, integrated graphics, and a network that reached a printer. They were the centre’s only fixed asset that was not a whiteboard, and they sat idle from four in the afternoon.
One constraint was settled before any arithmetic. After a previous incident with a homework app, parents had objected to student work leaving the building. A hosted interface was therefore not a cheaper option to be weighed. Uploading is a disclosure, and the disclosure was the thing being refused.
That left two shapes. Buy one machine with a sixteen-gigabyte graphics card, roughly what the centre spends on electricity in two months, and put a good model on it. Or run a small model on each of the eleven machines already in the lab.
Arvind, who taught physics, did not argue about it. He took thirty real doubts out of the register, covering definitions, unit conversions, two-step numericals and three questions where the right answer is that the question is missing information. Then he measured three configurations on all four numbers.
On the proposed new machine, a seven-billion-parameter instruct model at a four-bit quantization: peak memory 5.9 gigabytes, first token in 0.8 seconds, thirty-one tokens a second, and twenty-seven of thirty doubts answered acceptably by two teachers marking without knowing which machine produced what.
On a lab machine, a three-billion-parameter model at the same quantization: peak 2.4 gigabytes, first token in 2.6 seconds, nine tokens a second, twenty-two of thirty.
On a lab machine, the seven-billion model: peak 4.6 gigabytes, first token in eleven seconds, 2.4 tokens a second, twenty-six of thirty, and the machine could do nothing else while it ran.
The decision
The quality column said buy the machine. The third configuration said quality was not the whole story, because it answered nearly as well and was unusable.
And the number that decided it was not in the table at all. At nine at night there are not eleven students in the lab. There are sixty. One good machine is one queue, and a queue at nine is a student who has gone to bed. Eleven adequate machines answer everybody at once, worse.
The cost on that side was real: a model that gets twenty-two of thirty is wrong eight times, in front of children preparing for an examination, and wrong confidently.
There was a third option that never reached the table, and it is worth naming because most centres take it. Buy nothing, put a chat model’s website on the lab machines and tell the students not to type anything personal. It is free, it is better than everything measured here, and it fails the one constraint that was not negotiable. Meena’s position was that a rule students are asked to keep, about information they do not think of as personal, is not a control.
What made the measurement possible at all was that the thirty doubts already existed. Nobody had to invent test cases or imagine what a student might ask. They were in a paper register on the lab desk, written by the people the system was for, in the words those people use, including the three that were missing information, which turned out to be the ones that separated the configurations most sharply, because a small model at four bits will confidently answer a question that cannot be answered.
How it played out
They bought nothing for six weeks. The three-billion model went on all eleven machines behind a local server started at boot, with a browser page on each and a line at the top of it saying the answers are produced by a program and should be checked with a teacher. Every doubt the students marked as unhelpful was written to a shared file.
After six weeks the file said what the thirty doubts had hinted at: the small model was reliable on definitions, formulas and unit conversions, and poor on multi-step numericals, which is exactly where four-bit quantization damage concentrates. So they bought one machine, put the seven-billion model on it, called it the hard doubts station and sat a teacher beside it for an hour each evening.
One machine instead of eleven, and a decision made from twelve measured numbers rather than an argument about what feels fast. The thirty doubts stayed in a file in the repository. When a stronger four-billion model appeared four months later, re-running them took an afternoon.
Worth arguing about
The hosted option was ruled out before any measurement. Was that a technical decision?
One answer
No, and it is important that it was not treated as one. Sending student work to somebody else’s machine is a disclosure, and the parents had refused the disclosure rather than the latency. A constraint of that kind sits above the measurement: it removes options from the table rather than scoring them. Local was then the only shelf, and the whole question became which local configuration.
The third configuration scored twenty-six of thirty, almost matching the new machine. Which of the four numbers ruled it out, and what does that show about reporting one number?
One answer
Time to first token, at eleven seconds, and throughput at 2.4 tokens a second: slower than a person reads. Quality alone would have made it look like the cheap winner. Every configuration in this module trades along peak memory, first-token latency, throughput and quality at once, so any one of them can be improved while the system gets worse. Reporting all four is what made the comparison honest.
What did keeping the thirty doubts in a file, rather than in a notebook cell, buy the centre later?
One answer
A re-runnable decision. When a new model appeared, the question was not whether it felt better but whether it scored better on the same thirty items that the current choice had been made from, and that took an afternoon. The set also detects the failures that matter at that size (a model that has lost a capability, or a configuration that stopped producing usable answers) which is exactly what thirty examples are good for.
Test yourself6 questions on this module
You delete the familiar filenames under snapshots/ in the Hugging Face cache and no disk space is freed. Why not?
Why is bfloat16 usually preferred to float16 on hardware that supports both?
hf_device_map shows several layers on ‘disk’ and generation takes minutes per token. What is happening, and what fixes it?
At a fixed budget of about eight gigabytes, which usually performs better on general tasks?
A model that chats indistinguishably at four bits starts failing a JSON parser three weeks later. What is the mechanism?
You time a GPU generation loop with a plain Python timer and get a suspiciously high throughput figure. What did you actually measure?
Spaces: putting something in front of people
10 lessons · 1 h 25
You will be able toBuild, configure and debug a Space that other people can use (choosing the SDK, pinning versions in the README, holding secrets and state correctly, picking hardware against a sleep policy) and call an existing Space programmatically as an API instead of reimplementing it.
- 4.1A Space is someone else’s computer, already configured
- 4.2Forty lines from nothing to a public URL
- 4.3Blocks, when Interface is no longer enough
- 4.4Duplicate it, and the app becomes yours to change
- 4.5The README front matter is the deployment config
- 4.6Hardware, sleep, and the bill that runs while you are asleep
- 4.7The filesystem disappears, and what to do about it
- 4.8Streamlit, Docker and a Space with no server at all
- 4.9Every Space is an API you can call
- 4.10It built, it started, it is red
An illustrative case, built from documented patternsThe draft that appeared in somebody else’s browser
The Space took a person’s complaint, typed in Hindi, and produced a formatted application naming the right public authority and the right section. Farhan, a volunteer, had written it in about a hundred and fifty lines. It had run for four months on free hardware at roughly ten visitors a day and had never once misbehaved.
The tool existed because filing a Right to Information application is not hard and is easy to get wrong. Name the wrong public authority and the application is transferred, or lost, and thirty days become ninety. The Space did one useful thing: it read a complaint in ordinary words and produced a correctly addressed application with the right section quoted.
Then a radio programme mentioned it, and traffic went to four hundred a day.
On the third day an email arrived from a user in Gumla. He had clicked the draft button and received an application about somebody else’s ration card, with a name and a village in it.
The cause was one line near the top of the file. A list, declared at module level, holding the conversation so far. A Space is one process serving everybody, so a module-level variable is shared by every visitor on the internet. At ten visitors a day, spread across a working day, two people were almost never in the handler at the same time. At four hundred a day they were.
The decision
It was nine in the evening. Farhan was confident the fix took twenty minutes: move the per-person state into a session object and leave only the model shared.
Anjali Toppo, who ran the NGO, asked two questions before agreeing to patch and stay up. How many people had seen somebody else’s text? And would they know if they had?
Nobody could answer either. The Space kept no logs and no analytics, because the readme said nothing you type is stored and that had been a deliberate choice.
That absence was the finding, and it cut both ways. It made the incident impossible to investigate. It was also the reason the exposure stopped at whoever happened to be on screen at that moment, rather than sitting in a file on a container that anybody who could reach the machine could read.
The second decision was harder than the first. Saying nothing meant the radio push kept working. Saying something meant announcing, in the same week the NGO was asking people to trust it with a grievance about their own ration card, that one person’s grievance had been shown to a stranger.
Anjali’s decision about disclosure turned on what the NGO was asking people for. It asks people to write down a grievance about their own household: a pension that has not arrived, a ration card that was cancelled, a road that was paid for and not built. The whole product is a request to trust it with something that is nobody else’s business. An organisation making that request cannot decide, privately, that one breach of it was small enough not to mention.
There was also a version of the fix that Farhan wanted and did not do. He could have kept the Space up and pushed the patch straight to the running container, which would have taken the same twenty minutes and lost no traffic. Anjali refused it on the grounds that a patch applied to a live service, at nine at night, by a tired volunteer, with no way to tell afterwards whether it had worked, is how a small incident becomes a larger one. Taking it down made the test possible: two browsers, two sessions, and a check that they stayed apart before anybody else could reach it.
How it played out
They took it down that night and replaced it with a page saying the tool was paused because a fault could show one person’s draft to another, that nothing had been stored, and that it would be back the next day.
Farhan moved every per-visitor value into gr.State, which is per session, and left the model where it was, because a model is read-only and shared is the correct thing for it to be. He opened the Space in two browsers and checked the two sessions stayed apart before pushing. He turned flagging off, so nothing anybody typed could be written to disk by accident, and added one line above the fold in the readme rather than behind a link.
It was back in nineteen hours. Use in the following week was higher than the week before, and two people wrote to say the notice was the reason they tried it. The rule Farhan left as a comment at the top of the file is four words long: the model is shared, the person is not.
Worth arguing about
The bug survived four months of use. Why did neither testing nor the first four months find it?
One answer
Because a module-level variable behaves perfectly when there is one visitor at a time, which is the condition of every test the author runs and, at ten visitors a day, of nearly every real request too. The defect only appears when two people are inside the handler at once. That is why the rule is structural rather than empirical:
gr.Stateis per session, a module-level object is per process, and correctness under one user tells you nothing.The lack of logging prevented the investigation and also limited the harm. How should a Space like this one resolve that tension?
One answer
By separating what is needed to operate from what is a record of people. A counter of requests, or a log line with no user text in it, would have answered how many sessions overlapped without storing a single grievance. The commitment in the readme was about user text, and it could have been kept exactly while still recording enough to investigate. Collect the minimum, say what you collect, and make sure the minimum includes what you would need after an incident.
The model stayed a module-level object while the history moved. Why is that not inconsistent?
One answer
Sharing is correct for anything read-only and expensive, and wrong for anything about a person. The model is loaded once at import and never mutated, which is exactly what makes a Space fast: loading it inside the handler would reload the weights on every request. The conversation is mutated by whoever is speaking, so it has to be per session. The distinction is not module level versus not; it is read-only and shared versus written and personal.
Test yourself6 questions on this module
A Space answers in 200 milliseconds when you test it and takes 30 seconds for visitors. Which defect fits?
Two visitors to one Gradio Space start seeing each other’s conversation. What is the cause, and the fix?
You upgrade a Space to a rented GPU to test something, then close the tab. What happens to the bill?
A duplicated Space that worked in March fails to start in October with a TypeError about an unexpected keyword argument, and nothing was committed. What do you check first?
A Space builds, starts, and then the container dies with no Python traceback anywhere in the logs. What does that pattern mean?
You need a public Space’s function inside a nightly job over ten thousand rows. What is the responsible route?
Datasets, and the work that happens before any model
10 lessons · 1 h 27
You will be able toInspect, stream, filter and transform a dataset far larger than your RAM, query the Hub’s parquet conversion with SQL before downloading anything, run duplicate, leakage and class-balance checks on your own data, and publish it with a card stating provenance and licence.
- 5.1Do not download the dataset
- 5.2A dataset that behaves like a list and is not one
- 5.3Why a 300 GB dataset opens instantly
- 5.4Transforming a million rows without waiting an hour
- 5.5Query a dataset with SQL before downloading it
- 5.6Turning your files into a dataset
- 5.7The card is where a dataset stops being a file
- 5.8Media columns decode when you touch them
- 5.9Duplicates, leakage, balance, and label noise
- 5.10From a dataset to batches a trainer can eat
An illustrative case, built from documented patternsNinety-four in the lab, sixty-one in the field
The advisory’s classifier identified five cotton pests from a photograph a farmer sent from a phone. It reported ninety-four per cent accuracy on a held-out test split, and the press note said so.
The advisory itself was a genuinely good idea and popular. A farmer photographed a leaf, sent it, and got back a named pest and a recommendation within a few minutes, in Marathi, at no cost. Eleven thousand farmers had used it in the second season.
Over the following month the department’s own agronomists checked the advisory’s answers against what they found in the field. They recorded sixty-one per cent.
Nothing was wrong with the model. The split was the problem, and it had been made the way almost everybody makes one: randomly, over photographs.
A farmer reporting a problem does not send one photograph. He sends six: the same plant, the same field, the same light, minutes apart. A random split scattered those six across train and test. For a large share of the test set the model had already seen the neighbouring photograph of the same leaf, and what the ninety-four per cent measured was its ability to recognise a photograph it had effectively already been trained on.
An hour of checks that should have run before the training found two more things. Four thousand one hundred exact duplicates, nearly all of them one automated thumbnail resent by the intake system when a message failed. And a class balance nobody had written down: one pest was fifty-eight per cent of the rows, because collection had started in its season, while the rarest was three per cent, and the rare one was the one that costs a farmer the crop.
The decision
Arun Deshmukh, who led the team, had a number in a press note and a minister’s office that liked the number.
Re-splitting by contributor and retraining meant about two weeks, and it meant publishing a lower figure after a higher one, which in a government department is a specific and well-understood kind of cost. Somebody would have to explain, in writing, why the first number had been wrong.
Doing nothing meant the advisory carried on telling farmers something that was right three times in five, in a season, about a pest that eats cotton.
There was also a third thing on the table that nobody had asked for. The rare class at three per cent meant that whatever the headline accuracy said, the advisory’s performance on its most expensive case had never been measured separately at all.
Arun made one decision before any of the others that made the rest possible. He asked the agronomists to keep recording field agreement, weekly, whatever the model said. It cost each of them about twenty minutes a week and it is the only reason anybody knew there was a problem at all. A held-out test split can be wrong in a way that no amount of staring at it reveals; a measurement taken outside the pipeline cannot be fooled by anything inside it.
The duplicate check is worth its own sentence. Four thousand one hundred copies of one automated thumbnail is two things at once: a training set that over-weights a picture of nothing, and a test set that repeatedly asks the same non-question. It took one query to find and one line to remove, and it had been sitting in the data for two seasons while three people argued about model architectures.
How it played out
They re-split by contributor identifier rather than by photograph, dropped the exact duplicates, and retrained. Test accuracy fell to sixty-seven per cent: within six points of what the agronomists had measured in the field. That agreement was the first evidence the team had that their evaluation measured anything real, and Arun used exactly that argument with the minister’s office.
Per-class recall told the sharper story: eighty-one per cent on the majority pest and thirty-four on the rare, expensive one.
The correction went out as an update rather than a retraction. It said the earlier figure had been measured on a split that let the model see neighbouring photographs of the same plant, that the corrected figure was sixty-seven per cent, and that recall on the pest that matters most was thirty-four per cent and was now the work. It also announced a product change that came straight out of that number: the advisory would show two candidates and a button that sends the photograph to an agronomist, instead of one confident label.
Nobody at the department enjoyed the week. The advisory is still running.
Worth arguing about
Name the axis the split should have been made along, and state the general rule it follows.
One answer
By contributor, because a contributor sends many photographs of the same plant and the deployment has to generalise to farmers it has never seen. The general rule is to split along the axis your deployment must generalise across: by customer if you will see new customers, by document if chunks come from documents, by date if you are predicting forward, by speaker if the users are new speakers. A random row split is only correct when rows are genuinely independent, which they rarely are.
Why did the team treat ninety-four and sixty-one collapsing to sixty-seven as good news?
One answer
Because the laboratory number and the field number finally agreed. Before the fix, the evaluation measured something that did not exist outside the test split, so no decision could be taken from it. After the fix, a six-point gap between held-out measurement and field observation is close enough that the measurement can be used to compare the next model against this one. An evaluation that tracks reality is worth more than a high number that does not.
The rare class sat at thirty-four per cent recall and changed the product. Why is that the right number to act on, and overall accuracy the wrong one?
One answer
Because the classes are not equally consequential and they are not equally common. At fifty-eight per cent majority, a model can post a respectable overall accuracy while barely detecting the pest that destroys a crop, and the overall figure hides exactly that. Per-class recall names it. It also converts directly into a product decision: at thirty-four per cent you do not show one confident label, you show two candidates and a route to a human.
Test yourself6 questions on this module
You will make three training passes over a 200 GB corpus and you have 400 GB of free disk. Which is right, and why?
Shuffling a 40 GB dataset returns instantly and uses no extra memory. What makes that possible?
You need to turn 1,000 documents into 40,000 chunks. Which is true of map with batched=True?
A GROUP BY over one column of a 50 GB public dataset transfers only a few megabytes. What makes that possible?
A model scores 94 per cent on a random split of chunked documents and 61 per cent in deployment. What fits best?
Training runs without error, and the loss is completely flat from step one. Which of these produces exactly that?
Embeddings, and search that understands
9 lessons · 1 h 17
You will be able toBuild and measure a semantic search over your own documents: choosing an embedding model against your language and text length, chunking deliberately, normalising vectors for the similarity you use, adding a cross-encoder reranker, and reporting recall@k on questions you wrote yourself.
- 6.1A list of numbers where near means similar
- 6.2The library, and the four calls you need
- 6.3Picking the model, and reading MTEB without being fooled
- 6.4Cosine, dot product, and the normalisation that connects them
- 6.5Chunking decides what your search can find
- 6.6NumPy first, FAISS second, a database rarely
- 6.7The second pass that fixes the first
- 6.8One space for pictures and words
- 6.9Thirty questions, and the numbers they give you
An illustrative case, built from documented patternsThe part number the search could not find
Nilesh, the service manager, wanted an engineer standing next to a broken machine to find the right procedure on a phone. A consultant had quoted for a hosted vector database with an annual licence at about a quarter of the department’s software budget.
The manuals were PDFs, some of them scanned from the 1990s and some produced last year, covering fourteen machine families. The service team’s working method was a WhatsApp group and a man called Ramesh who had been there twenty-two years and knew where everything was. Ramesh was retiring in eighteen months, which is what started the project.
Priyanka, a trainee on a six-month placement, built the cheap version first, because it was two days rather than a procurement cycle. She split the manuals into chunks on their headings, embedded them with a ninety-megabyte model, saved the vectors to a file, and searched them with one matrix multiply. Thirty-one thousand chunks, a few milliseconds a query, no server.
On questions phrased in words it worked well. How do I reset the servo after a power cut returned the right page.
On the queries the service team actually types, it failed. TX-4410 seal kit. Error E-207. The 2019 machine at Kulkarni’s. An embedding model places a part number roughly nowhere in particular, because the number carries no meaning it was trained to place, and the vector for TX-4410 is not close to the page that contains TX-4410.
The decision
Nilesh had a quote in front of him and a trainee asking for two more days.
The quoted product genuinely offered things the file did not: metadata filtering, incremental updates, several services sharing one index, and somebody to telephone when it broke. None of those was the problem the service team had. But a trainee on a placement is a support contract with an end date, and Nilesh had been burned before by a system whose author left.
What settled it was thirty questions. Priyanka took them out of the search log, kept the phrasing exactly as engineers had typed it, and recorded for each one the page that should come back. Four configurations, one afternoon.
Embeddings alone: the right page in the top five for fifty-three per cent of questions. Keyword ranking alone: sixty-one per cent, strong on codes and weak on paraphrases. Both, with their ranked lists merged: eighty-four per cent. Both plus a small cross-encoder rescoring the top fifty results: eighty-eight per cent, and the proportion where the right page was first went from forty-one per cent to sixty-six.
Two extra dependencies, both free, both installed with one command. About a hundred and thirty milliseconds a query, of which the rescoring step was a hundred.
The thirty questions took Priyanka a morning and taught her something the measurements did not. Nearly half the queries in the log contained an identifier of some kind: a part number, an error code, a machine serial, or a customer’s surname standing in for a machine. That distribution is a fact about the service desk rather than about retrieval, and it explains why the keyword method on its own beat the embeddings on its own, which is the reverse of what every tutorial had led her to expect.
She also checked one thing that is easy to skip. She printed the lengths of the vectors after encoding, to confirm they were all 1.0 and that the search was therefore comparing angles rather than being biased toward long documents. They were not normalised in her first version. Fixing that one argument moved recall by three points before any of the other work, and it is the kind of defect that otherwise shows up only as a system that is slightly worse than expected and is never diagnosed.
How it played out
They did not buy the database. They did write down, in the repository beside the code, the two conditions under which they would: more vectors than fit in memory on a service laptop, noted as a few million rather than the thirty-one thousand they had, and a need for filtered search across product lines with different access rights. Both were written as numbers, so the decision could be revisited later without anybody having to win the argument again.
The largest single improvement came from the rescoring model, which is ninety megabytes and runs on an ordinary processor. The second largest came from adding keyword search back alongside the embeddings, which most people assume is the old thing they are replacing.
The file of thirty questions has since outlived two changes of embedding model. Priyanka’s placement ended in March. The system was still running in December, and the person who took it over could tell whether their changes helped, because the questions were in the repository.
Worth arguing about
Why does an embedding model fail on TX-4410 while keyword ranking succeeds?
One answer
An embedding places text by meaning learned from training data, and an arbitrary part number has no meaning to place: its vector lands in an unhelpful part of the space and is not near the page that contains it. Keyword ranking matches the literal token, which is exactly right for identifiers, error codes and surnames. The two methods fail in different places, which is why merging their ranked lists beats either one and why hybrid retrieval is a sensible default rather than an advanced technique.
The database’s features were real. Why were they not a reason to buy it?
One answer
Because none of them addressed the failure the team actually had, which was retrieval quality on identifier queries. Filtering, incremental updates and shared indexes are operational features, not search-quality features; buying them would have left the part-number problem exactly where it was, at a quarter of the budget. The conditions they wrote down are the ones under which those features start to matter, and at thirty-one thousand chunks none had been met.
They wrote their buying conditions as numbers. Why does that matter more than the decision itself?
One answer
Because it makes the decision re-openable without re-arguing it. A stated threshold (a few million vectors, or filtered access across product lines) can be checked against reality by anybody, including the person who inherits the system. A decision recorded as a preference has to be defended by the person who made it, and when that person’s placement ends the reasoning leaves with them.
Test yourself6 questions on this module
A query about cancelling a subscription retrieves a passage about renewing one, near the top. What does that demonstrate?
Vector norms print as 8.3, 11.2 and 6.7, and dot-product search is slightly worse than expected. What is wrong?
A BGE or E5 family model scores several points below its published numbers on your own set. What is the common cause?
Chunks are cut at 1,500 characters. English chunks fit the model’s window and Hindi chunks are silently truncated. Why?
Your bi-encoder’s recall@50 is 0.92 and its recall@200 is 0.93. What follows for the reranking step?
You have 31,000 chunks of 384 dimensions and a web request to answer. What should the first index be?
Training and adapting, with the libraries the Hub is built on
9 lessons · 1 h 17
You will be able toDecide from evidence whether a task needs fine-tuning at all, run a LoRA or QLoRA adaptation with Trainer or SFTTrainer on a single free GPU, read a loss curve well enough to name what is wrong, and publish an adapter a stranger can apply to the base model.
- 7.1Three cheaper things to try first
- 7.2Trainer, and what it saves you writing
- 7.3The six arguments worth understanding
- 7.4LoRA: training 0.1% of the parameters
- 7.5QLoRA, and fine-tuning a 7B model on a free GPU
- 7.6TRL, and training on conversations
- 7.7Watching a run you cannot sit next to
- 7.8Loss is NaN, memory is gone, nothing is learning
- 7.9Shipping the thing you trained
An illustrative case, built from documented patternsThe fine-tune that was two different problems
Harsha, the founder, wanted a fine-tune. The base model answered in Kannada, but in a register that read like a textbook, and it did not know the chain’s warranty rules, which are rewritten twice a year.
The chain runs forty-one service centres across two states. A customer writes in Kannada, usually from a phone, usually annoyed, and usually about a machine that is under warranty or has just fallen out of it. The assistant was meant to draft the first reply, which an agent would then send or rewrite.
Divya, the engineer, asked the question the module opens with: is the gap knowledge or behaviour. The honest answer was both, and the two halves needed opposite treatments.
The warranty rules are facts. Facts trained into weights come back fluent, confident and subtly wrong, because the model learns the shape of a policy document rather than its contents, and they change in April and October. That half became retrieval: sixty pages of policy, chunked on headings, a small multilingual embedding model, and thirty questions taken from the call log.
The register is behaviour: how a service agent speaks to a customer whose washing machine has been broken for four days. No prompt got it reliably, and few-shot examples got it for two turns and then drifted. That half was a fine-tune.
The decision
The decision with a cost on both sides was about the training data, and it was not a technical one.
Forty thousand historical chat transcripts existed. They were free, they were already labelled by who wrote what, and they were full of exactly the register they wanted. They were also full of customer names, addresses, telephone numbers, and, in a few hundred cases, card details pasted by customers who had been told not to paste card details.
The alternative was six hundred examples written by hand with two of the chain’s best agents, which meant eight working days of two people who were needed on the service floor, in a month the chain was short-staffed.
Harsha wanted the forty thousand. Divya ran the check rather than the argument: an automatic redaction pass over a sample of five hundred transcripts, then reading them. The pass caught the telephone numbers and the obvious identifiers. It missed a full address written across three lines of chat, and it missed the sentence where a customer named their employer, which in a small town names the person.
Automatic redaction misses context. Reading five hundred rows is what told them so.
Before any of this Divya spent a morning on the cheaper options, in order, because the module says to. A larger model with a better prompt closed perhaps half the register gap and none of the warranty gap. Five worked examples in the prompt closed most of the register gap for the first two turns of a conversation and then drifted, which is a known shape and not a fixable one. Only after both of those had been tried and written down did fine-tuning become the answer rather than the instinct.
The six hundred examples cost more than the model did, and that is the ordinary case rather than a surprise. Three hours of a free GPU session against nine days of two experienced agents is not a close comparison. It is also why the data went into a repository and the adapter did not particularly matter: when the base model is superseded in a few months, the adapter expires and the six hundred conversations do not. Retraining against a new base is then an afternoon rather than another nine days on the service floor.
How it played out
They wrote six hundred examples by hand, over nine days rather than eight, with two agents and a rule that every example had to be a conversation one of them had actually had.
Before the real run Divya trained on ten of those examples for many epochs to see whether the model could memorise them. It could not, and the reason was that her prompt masking was wrong: loss was falling on the customer’s messages as well as the agent’s, so the model was being graded on writing complaints. Ten minutes found what three hours of training would have produced as a confusing result.
The run itself was a rank-sixteen adapter over a four-bit base on a free session, about three hours. Training and validation loss both fell and levelled, with no divergence. Then the check that is not a number: twenty held-out inputs, generated and read aloud. The model had learned the register, and it had also learned to promise a technician within twenty-four hours, because the agents’ best conversations tended to end that way. Twelve examples were rewritten to remove the promise and the run was repeated.
The adapter is thirty-four megabytes. The warranty rules stayed in the retrieval index and were updated in April by replacing six files. The six hundred examples and the thirty questions went into a private repository, which is the part that will survive the next base model.
Worth arguing about
Why did the warranty rules belong in retrieval and the register belong in the weights?
One answer
Fine-tuning teaches behaviour; retrieval supplies knowledge. A policy is knowledge: it must be exact, it must be citable, and it changes twice a year, so training it in produces confident paraphrases that cannot be updated without retraining. A register is behaviour: it is a way of writing rather than a fact, it cannot be looked up, and it is precisely what a few hundred well-chosen examples teach. Splitting the problem this way let each half be fixed by the cheaper method.
The ten-example overfit test cost ten minutes. What did it find, and what was the alternative cost?
One answer
It found that the prompt positions were not masked, so loss was computed on the customer’s turns as well as the agent’s. A model that cannot memorise ten rows is not learning, so the test separates a pipeline bug from a data problem before any real run. Without it the three-hour run would have completed with a falling loss curve and produced a model that writes complaints rather than answers them: a failure that looks like success in every logged number.
The model promised a technician within twenty-four hours. What caused it, and why does only reading the output catch it?
One answer
The examples were the agents’ best conversations, and their best conversations tended to end in that promise, so the habit was in the data. A model copies the register it is shown, including commitments. Loss cannot see it: a promise is fluent, in the right language and in the right style, so validation loss improves while the model acquires a liability. Generating twenty held-out answers and reading them is the only check that surfaces a learned habit you would have rejected.
Test yourself6 questions on this module
Your assistant does not know this year’s refund policy. Which is the right tool, and why?
LoRA cuts training memory for a 7B model by far more than the trainable parameter count alone suggests. What is the main reason?
Which learning rate suits a LoRA adapter, and why does it differ from full fine-tuning?
You load a 4-bit base, attach LoRA, start training, and the loss barely moves. Which omission fits?
You train on ten examples for many epochs and the model cannot reproduce them. What does that tell you?
Training loss keeps falling while validation loss turns upward from about step 350. What should you do?
Depending on it, and publishing so others can
10 lessons · 1 h 28
You will be able toState what a model’s licence permits for your intended use and where the question is unresolved rather than answered, choose between a provider, a dedicated endpoint and your own hardware on stated cost and privacy grounds, judge a model with thirty examples of your own, and publish work a stranger can audit and reproduce.
- 8.1A token in a public Space is a token you have given away
- 8.2What the licence tag does and does not settle
- 8.3The leaderboard is not measuring your task
- 8.4The evaluation that decides it
- 8.5Renting the compute instead of owning it
- 8.6When transformers stops being the right server
- 8.7Publish it so a stranger can use it without writing to you
- 8.8Writing a card somebody can audit
- 8.9Pull requests, discussions and being useful
- 8.10Still working next year
An illustrative case, built from documented patternsThree weeks before launch, the base was not theirs to sell
The model at the centre of the product was a fine-tune published by an individual. Four hundred thousand downloads, a readable card, a demo Space that worked on a phone, and a licence tag reading apache-2.0. The studio had built on it for four months.
The tool itself was small and good. A seller photographs a kurta on a bedsheet in a room with one window, and gets back a clean cut-out on a white background with the light corrected. Two hundred rupees a month, aimed at people selling forty items rather than four thousand.
The check happened by accident. A prospective customer’s procurement form asked for a statement of every third-party component and its licence, with a signature underneath.
Ritu, the partner filling it in, opened the model page and followed the front matter. The fine-tune named a parent. The parent named another parent. The third repository, with no parent of its own, was under a non-commercial research licence.
The apache-2.0 tag at the top of that chain was either a careless tick or a sincere belief that an adapter is a separate artefact. It made no difference which. A fine-tune inherits its base model’s terms whatever its own card says, and the studio was three weeks from charging money for a derivative of a model that may not earn money.
The decision
Swapping meant a permissively licensed alternative. Two existed. On the studio’s thirty test photographs (textiles, brass and steel, shot on phones in the back rooms of Gujarati sellers, which is the case no public benchmark contains) the better of the two scored four points worse, and the loss was concentrated in the hard cases: fine fabric edges, and hair on a model’s forearm.
Launching anyway meant shipping. Nobody would notice on day one. The uploader probably did not know either. The risk is not a letter in the first week; it is a customer’s own procurement form asking the same question in a year, when the tool sits inside forty shops’ workflows and the answer has to be no.
They did write to the uploader to ask. Eight days, no reply, which is what an unpaid maintainer of a popular repository usually has time for.
So the choice was four quality points and eight days of schedule, against a dependency they could not answer a question about, in a product other people were about to put in their shop window.
The studio had done a version of this check at the start, which is why the mistake is worth studying rather than dismissing. Four months earlier somebody had opened the model page, seen apache-2.0 in the sidebar, and written it in a notebook. That is the check most people perform and it is the wrong one, because the tag describes an intention and the chain describes an entitlement. Nothing about the page announces that the badge at the top is only the first link.
Ritu also asked what the honest fallback would have been if no permissive alternative had existed at any quality. The answer they arrived at was to charge for the part they had made rather than the part they had borrowed (the pre-processing, the batching, the seller-facing workflow) and to let the customer bring their own model or run the non-commercial one themselves for their own use. It was not needed. Writing it down took ten minutes and it turned an argument about whether to ship into an argument about which of two lawful products to ship, which is a much easier argument to have three weeks before a launch.
How it played out
They swapped, and launched eight days late.
The four-point gap turned out to be two once they spent a day on the pre-processing step. The permissive model was more sensitive to input resolution, which is a fixable property rather than a capability gap, and they only found that because thirty photographs is few enough to sit down and look at all eleven failures one at a time.
Three practices came out of that fortnight and stayed. A two-line inventory per model, recording repository, revision hash, licence, the date it was read and where it runs. A rule that licence checking means following the base model field until a repository has no parent, rather than reading the badge on the page. And a card on the small pre-processing Space they published themselves, saying what it was built from, what it does badly, and that the licence of their code is not the licence of the model inside it.
The launch slipped eight days. The inventory takes about a minute per model and has been consulted twice since, both times by somebody else’s procurement form.
Worth arguing about
What does an apache-2.0 tag on a fine-tune actually settle, and what does it not?
One answer
It states what the uploader intends to grant over their own contribution, and it is indexed by the Hub so people can filter on it. It cannot grant rights the uploader never held. A fine-tune is a derivative of its base, so the base’s terms travel with it however the child is tagged, and the check is to follow the base model field until there is no parent. It also says nothing about the training data or about who owns the outputs, both of which are genuinely unresolved and differ by country.
Why could thirty photographs of their own decide the swap when a leaderboard could not?
One answer
Because the thirty contained the case the studio is paid for: phone photographs of textiles and brass in poor light, which no public benchmark includes. A leaderboard rank is measured on somebody else’s distribution and narrows four hundred candidates to a shortlist; it cannot pick between two shortlisted models for a specific job. It also gave them failures to read, and reading the eleven failures is what found the resolution problem.
The four-point gap became two after a day of work. What does that say about deciding between models on a single number?
One answer
That a headline score bundles the model with everything around it (pre-processing, prompt, generation settings, input resolution) and a gap is not evidence of which part is responsible until you look. Reading the failures separated a fixable input problem from a capability difference. At thirty items a four-point gap is also close to the noise floor, so acting on it as a capability judgement, in either direction, would have been unsound.
Test yourself6 questions on this module
Your Space gets a 403 on a gated model whose terms you accepted in a browser. What is wrong?
A model is published under Apache 2.0. Which question does that licence actually settle?
At 100,000 tokens a day for a small open model, which is cheaper, and why?
A transformers loop behind a web framework sits at 30 per cent GPU utilisation with ten concurrent users. Which change addresses the cause?
Model A scores 27 of 30 and model B scores 26 of 30 on your own evaluation set. What may you conclude?
Which pair, pinned, removes most of the failures where something worked in March and broke in October?
What this course is
The Hub is a host for git repositories that happen to contain neural networks, and that framing explains the rest. You will read a model card licence-first, tell the four shelves apart, run and duplicate Spaces on free hardware, keep tokens out of public code, load models and stream datasets without filling your disk, judge a leaderboard sceptically, and publish a card a stranger can act on.
Who wrote it
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.
More in Make things
- Making Things with AIImages, video, voice and music: how they work, where they break, who owns them.
- Design, before Any SoftwareHierarchy, type, colour and spacing: the part of design you can do with a pencil, before any software.
- Editing an ImageLayers, masks, curves and export: the craft behind a photo that still holds up at full size.
- Vectors, Logos and Things That Get PrintedDraw curves that hold at any size, then hand a press a file it will not send back.
- Image Generation, in PracticeReference images, inpainting, character LoRAs, upscaling, and the licence nobody read.
- Editing VideoCuts, sound, colour and export, taught on DaVinci Resolve, which costs nothing.
- Video with AIPlan the shots, generate ten takes, throw eight away, and cut what is left in a real editor.
- Motion, Sound and MusicWhy nothing in the world starts at full speed, and why a blanket beats a better microphone.
- Making It Say SomethingClean work that communicates nothing is the commonest self-taught failure, and it is fixable.
- Working for YourselfSix portfolio pieces, twelve cold emails, a rate with arithmetic behind it, and a deposit before you start.