Understand experiments · Lesson 09
Expose the retained fraud prediction model
The application team wants a fraud prediction, not another training run. The video separates the notebook's training path from its typed prediction function and binds the exact retained model to REST and MCP.
By the end of this lesson
- Identify the prediction's model, feature helper and serving dependencies.
- Make the four recorded serving choices explicit.
- Observe a stale model refusal and restore the selected bytes.
Watch the lesson
English · 6:49 · Recorded with Apizr 0.4.6 development candidate.
Chapters in this recording
| Time | Chapter |
|---|---|
| 00:00 | Share prediction without rerunning training |
| 00:26 | Draw the serving boundary first |
| 00:56 | Inspect what predict really needs |
| 01:54 | Retain the model that will be selected |
| 03:00 | Four choices you must be able to explain |
| 03:34 | Generate the two explicit bindings |
| 04:46 | Variant: replaced model bytes block exposure |
| 05:41 | Your turn: a newer model appears at the path |
| 06:07 | Bind evidence before you discuss deployment |
| 06:39 | Continue with the written guide |
Read the recording transcript and commands
This is the retained English narration, in the original chapter order. Commands keep the recorded version and prerequisites; setup additions in the lesson are labelled separately.
00:00 — Share prediction without rerunning training
An application team wants to call the fraud model. Giving them the whole notebook leaves important questions unanswered: which Run, which model, which function and which dependencies? We will use a successful Run from the same example to generate one selected prediction interface. By the end you will explain why a training artifact, a serving selection and a deployment are different things.
00:26 — Draw the serving boundary first
The notebook trains inside a main guard and defines prediction separately. Importing the prediction module must not retrain the forest. The prediction needs the selected model, joblib, scikit-learn and the feature-order helper. Pandas belongs to training here. These concrete dependencies are more useful than exposing every callable found in a repository. Our public surface will be one prediction accepting a typed transaction.
00:56 — Inspect what predict really needs
Read the actual predict function. It loads the saved model and uses the shared helper to construct the feature row in the expected order. The transaction has an amount, a velocity, a foreign-transaction flag and account age. The return carries a classification and probability. These are ordinary workload choices; Apizr does not invent the prediction logic. The main guard keeps training out of module import, while dependencies still need explicit serving evidence.
python lab.py source predict
python lab.py source settings
01:54 — Retain the model that will be selected
For this independent lesson we start from a fresh copy, select cohort B and execute the reviewed notebook. We give the model its explicit logical name. Read the retained output identity before generation: this is the artifact we intend to bind. The training marker helps us distinguish an experiment-side file from a serving selection. Generating the interfaces later must not be treated as a new training run.
cp data/transactions-b.csv data/transactions.csv
apizr experiment run fraud_detection.ipynb --output model=artifacts/model.joblib --format json > run-b.json
python lab.py run run-b.json
03:00 — Four choices you must be able to explain
Name the four choices before typing the expose command. The Run digest selects retained evidence. The capability identifier selects prediction. The artifact name selects the recorded model bytes. The dependency wheelhouse supplies evidence for the external packages. Conditional allowance acknowledges that external code remains opaque; it is not a certificate of safe initialization. Missing or stale required evidence is a reason to stop and investigate.
03:34 — Generate the two explicit bindings
We create a narrow operator grant for this copied local project. Expose then selects the Run, prediction capability, model and scientific dependencies. The interface option changes between REST and MCP, while the selected Run stays the same. These commands create local bundles. They do not start a server or contact a registry. Keep the complete generated reports so that a reviewer can inspect the actual selection and its dependency evidence.
python -c 'import json; from pathlib import Path; Path("operator.json").write_text(json.dumps({"schema":"apizr.operator-policy/v1","grants":[{"adapter":"repository","operation":"analyze","target":{"kind":"local","root":str(Path.cwd())},"permissions":["source.analyze"]}]}))'
RUN_B=$(python -c 'import json; print(json.load(open("run-b.json"))["run_digest"])')
for interface in rest mcp; do apizr experiment expose "$RUN_B" --root . --operator-policy operator.json --capability python:fraud_detection:predict --interface "$interface" --artifact model --dependency scikit-learn --dependency joblib --dependency-wheelhouse "$SCIENCE_WHEELHOUSE" --allow-conditional --output-dir "bundles/$interface" > "$interface-exposure.json"; done
python lab.py binding
04:46 — Variant: replaced model bytes block exposure
Test the binding against a stale artifact, not just a valid model. We save the selected model, then deliberately alter the bytes at its current path. The original Run still identifies the old model. Expose must refuse the mismatch instead of binding the new bytes under the old evidence. We read the actual refusal and restore the selected bytes, checking their digest. This is why an artifact path, an artifact identity and a retained artifact copy are three separate operational concerns.
RUN_B=$(python -c 'import json; print(json.load(open("run-b.json"))["run_digest"])')
cp artifacts/model.joblib artifacts/original-model.joblib; printf "altered" >> artifacts/model.joblib
Expected refusal: exit 2.
apizr experiment expose "$RUN_B" --root . --operator-policy operator.json --capability python:fraud_detection:predict --interface rest --artifact model --dependency scikit-learn --dependency joblib --dependency-wheelhouse "$SCIENCE_WHEELHOUSE" --allow-conditional --output-dir bundles/stale > refused.json 2> refused.log
python lab.py refusal
python lab.py restore-model
05:41 — Your turn: a newer model appears at the path
Pause here. Another training run overwrites model dot joblib after the selected Run was recorded. Should the original Run silently pick up that newer model when exposed? Which fact would you compare before continuing? Also explain why training completed dot text should not become a serving dependency merely because it exists in the experiment folder.
06:07 — Bind evidence before you discuss deployment
A different file at the same path is not the selected recorded model. Preserve and verify the intended bytes instead of silently substituting a newer artifact. Only selected serving artifacts belong in the binding. REST and MCP describe two interfaces around this selection; real calls, runtime isolation, operations and deployment need their own qualification. You now have a reviewable handoff with a clear serving boundary.
06:39 — Continue with the written guide
The current written guide remains authoritative. Continue there for the detailed steps. Thank you for following Apizr Academy.
1. Read the actual serving boundary
Use a fresh fraud lab from the recorded materials, its scientific environment and a wheelhouse prepared for that environment as described in the data-science guide. Set SCIENCE_WHEELHOUSE to that actual wheelhouse path.
python lab.py source predict
python lab.py source settings
cp data/transactions-b.csv data/transactions.csv
apizr experiment run fraud_detection.ipynb --output model=artifacts/model.joblib --format json > run-b.json
python lab.py run run-b.json
fraud_detection.predict accepts a typed transaction with amount, velocity, foreign flag and account age. It loads the selected joblib model and uses features.py to preserve feature order. Training stays behind the Python main guard. Joblib and scikit-learn are serving dependencies here; pandas belongs to training.
2. Make the four choices and inspect both bindings
Choose the retained Run, python:fraud_detection:predict, the logical artifact model, and scientific dependency evidence. Create the copied project's analysis grant, then expose the two interfaces:
python -c 'import json; from pathlib import Path; Path("operator.json").write_text(json.dumps({"schema":"apizr.operator-policy/v1","grants":[{"adapter":"repository","operation":"analyze","target":{"kind":"local","root":str(Path.cwd())},"permissions":["source.analyze"]}]}))'
RUN_B=$(python -c 'import json; print(json.load(open("run-b.json"))["run_digest"])')
for interface in rest mcp; do
apizr experiment expose "$RUN_B" --root . --operator-policy operator.json --capability python:fraud_detection:predict --interface "$interface" --artifact model --dependency scikit-learn --dependency joblib --dependency-wheelhouse "$SCIENCE_WHEELHOUSE" --allow-conditional --output-dir "bundles/$interface" > "$interface-exposure.json"
done
python lab.py binding
--allow-conditional acknowledges opaque external dependency code; it is not a certificate of safe execution. The recording inspects generated local bundles. It does not start a server, demonstrate runtime isolation or deploy a prediction service. The training marker is absent from both serving bundles.
3. Replay the stale-model variant
cp artifacts/model.joblib artifacts/original-model.joblib
printf 'altered' >> artifacts/model.joblib
apizr experiment expose "$RUN_B" --root . --operator-policy operator.json --capability python:fraud_detection:predict --interface rest --artifact model --dependency scikit-learn --dependency joblib --dependency-wheelhouse "$SCIENCE_WHEELHOUSE" --allow-conditional --output-dir bundles/stale > refused.json 2> refused.log
The recorded refusal is run_artifact_changed, exit 2. Restore the saved model with python lab.py restore-model; the helper verifies the selected digest again.
Practice
Answer the filmed question: a newer training run replaces model.joblib. Should the original Run silently use it? Which identity should you check, and why is the training marker not a serving dependency?
Check your reasoning
A path is not the selected model identity. Preserve and verify the matching bytes instead of substituting a newer artifact. Only the selected serving artifacts belong in the binding. A training-side marker's presence in the experiment folder does not make prediction depend on it.