DEV Community

Cover image for The weights were MIT. The training data wasn't.
Matt Cockayne
Matt Cockayne

Posted on Originally published at phpboyscout.uk

The weights were MIT. The training data wasn't.

Most of what's written about picking a machine-learning model is about accuracy. Benchmarks, latency, memory footprint, how it does on the awkward inputs. All useful stuff, and for me it comes second.

The first filter is the licence file, and it will throw out a model with better numbers than anything else you tried without so much as an argument.

Three licences in a trench coat

A model's licence turns out to be three facts, and they're allowed to disagree with each other.

There's the licence on the code that trains and runs it. There's the licence on the weights, which is a separate artefact and frequently a separate grant. And there's the licence on the data the weights were trained on, which hardly anyone checks and which is where the interesting problems live.

Then there's a fourth question, which isn't about licensing at all and bites just as hard: which artefact did you actually pin? The model in your lockfile came from a URL, and that URL is quite often someone's re-host rather than the project itself.

krites is a photo culler that runs entirely on your own machine. It's used by a working photographer, which means "research use only" is not a footnote I can file under later. Five models come up below, and each one landed somewhere different.

Permissive weights, non-commercial training data

The object-removal feature runs on a pair of inpainting models. LaMa is Apache-2.0. MI-GAN is MIT. On the face of it, both fine... until you read one more layer down.

Both are trained on Places2, whose terms are non-commercial and no-redistribution. That's written up in the repo's NOTICE.md under each model, flagged to be read before commercial distribution rather than filed in a licence appendix.

So does a set of weights inherit the terms of the data it was trained on? That question is unsettled, and a blog post isn't where it's going to get settled (least of all mine). Upstream released the weights permissively regardless, which is either a considered position or something that slipped past everyone, and I can't tell which from where I'm sitting.

What I could do was write down where the uncertainty sits, next to the model, so the next person to read it starts where I finished rather than where I started. And the two inpainters now sit behind two locks. remove.enabled turns object removal on, and it still won't run until remove.accept_licence_risk records that someone has explicitly accepted the Places2 terms. I named the second key after the risk instead of the feature, so you can't switch it on without reading what you're switching on.

And MI-GAN isn't pinned to the project at all

MI-GAN is MIT where it lives, at Picsart's research repo. The artefact krites pins is a Hugging Face re-host, and that re-host ships no licence file whatsoever!

That's a strange position to be in. The upstream is unambiguous, the copy in the lockfile is unlicensed, and there's also a GPL-3.0 mirror of the same model floating about that would be actively harmful to pick up by mistake.

What the notice records instead is a hardening task, written down rather than smoothed over: build our own export from the MIT repo, mirror it somewhere we control, ship it with the licence, and never pin the licence-less re-host or the GPL one.

It's not a satisfying resolution, but it's a true one. I'd rather the notice file carried the untidy cases than only the ones that make me look organised.

The non-commercial one, and what I actually did about it

The expression estimator originally read eye-openness and a geometric smile from InsightFace's 2d106det landmark model. Good model... non-commercial licence.

The obvious move is to delete it. I didn't. Well... I came closer than the code suggests.

What went away was 2d106det on the default path. A self-contained MediaPipe FaceMesh pass now produces the blendshapes that give eyes, facing, and a trained smile and valence signal, and it needs no non-commercial component at all, so the default stack is Apache and MIT the whole way down (9195e8a). The landmark strategy is still there as an opt-in you have to go and select.

My call at the time was that it had to go, because the risk was getting too great, and what shipped isn't quite a deletion. Both are true, and I'm fine with the gap between them.

The risk I meant is a product risk. I want krites to be a proper product one day, and non-commercial terms on anything in the shipped default is a thing I'm simply not prepared to carry into that. So the default had to be clean. It didn't follow that the capability had to go, and someone who goes and selects the landmark strategy is making a call about their own use, which is theirs to make and not mine to make for them.

The second reason matters more to me. I put out a fair amount of open source myself, nearly all of it MIT or BSD, which is as permissive as licences get. There may well come a day when I need to be more restrictive than that. Do unto others. If I want my terms respected when they're inconvenient to someone, I don't get to treat someone else's as a formality because they're inconvenient to me.

That's a fine speech, and I should probably climb down off it before someone gets hurt. That MI-GAN pin, with no licence file anywhere near it? It's still in the lockfile!

It's a stopgap with a job ticket against it (not a clean position, I know), and if a takedown notice ever lands I'll comply, and I'll have earned it.

Do unto others... mostly. I'm nearer that line than is comfortable.

The same thinking made the face detector pluggable, with YuNet from OpenCV Zoo as the default. MIT, and behind an interface.

Every model behind an interface

Every model in krites now sits behind an interface, and I'll own that it's a hedge more than a design flourish.

Extensibility for its own sake is how you end up with six interfaces and one implementation each (I have done it, more than once). This is different. A dependency that can be revoked by someone changing a licence file, or pulling a model, or a court deciding something about training data, is a dependency you need to be able to swap in an afternoon. The interface is the risk control there, and the more of them you have, the more you can test and evaluate over time and refine what you think.

The other part of it's boring and, I suspect, usually gets built after a scare. krites-models exists to fetch, convert, parity-check, checksum and sign the models, and to publish a provenance.json recording each one's exact upstream, version and licence.

Once you've had to move a model off your default path over its terms, "which licences am I currently shipping?" becomes a question you want to be able to print rather than remember. Mine prints, and it will keep printing that unlicensed re-host until I've built my own export.


Originally published at phpboyscout.uk on 1 November 2026.

Top comments (0)