Celsius to Kelvin

A regression whose answer you check with a subtraction.

This is a one-input tabular regression: you give it a temperature in degrees Celsius and it returns the same temperature in kelvin. The point of publishing it is not that it predicts well β€” it is that you already know the right answer (K = C + 273.15), so you can check every number below with a calculator instead of trusting a metric.

Built with MatrixAI, which turns a written specification into a neural model, trains it, and exports a package that predicts with no MatrixAI installed. It is one of the three reproducible cases at matrixaistudio.org/casos.

MatrixAI can also build a model from a plain-language description. This case was not made that way: it was made from the .mxai and the recipe published here.


1. Check it yourself, before anything else

Every number in this column comes out of a published file. The right-hand column is a subtraction anybody can do.

Input C + 273.15 What the package returns Difference
βˆ’40 Β°C 233.15 233.15000236034393 2.36eβˆ’06
0 Β°C 273.15 273.15000146627426 1.47eβˆ’06
50 Β°C 323.15 323.1500059366226 5.94eβˆ’06
100 Β°C 373.15 373.1500029563904 2.96eβˆ’06

Source: βˆ’40, 0 and 100 are the literal step-4 output in salida.txt; 50 is expected_output.json in this package (sha256 4b16dcefa0328bd4ce9e5cee18b52b7eb6b40d06d5873a8a38f705594bbc1476), which is the value predict.py --input example_input.json is supposed to print. The Difference column is arithmetic done here on those two columns, not a figure any file publishes.

The residual is float32 rounding, not model error, and you can confirm that too β€” see Β§6.

Where the four inputs come from. These are simply the inputs the case publishes: entrada.json (0 Β°C), entrada_100.json and entrada_-40.json at matrixaistudio.org/casos/kelvin/, plus 50 Β°C, which is the example_input.json that ships inside the package. There is nothing special about them beyond the fact that you know the right answer by heart. The model declares celsius: Scalar[-50, 150] (model.mxai, inference_spec.json), and all four sit inside that range.

None of the four isolates a single parameter, and it is worth knowing why. W1 and b1 are not a slope and an offset in degrees: predict.py normalizes the input to (C + 50) / 200 first, so at 0 Β°C the model does not see 0, it sees 0.25 β€” and W1 moves the answer there too. Measured on this package by perturbing W1 by 1 % and recomputing in float32: W1 = 0.8 gives K(0) = 273.15000146627426, while W1 = 0.808 gives 273.6500024795532 β€” half a kelvin away at the input where a slope error is supposed to be invisible. What the four inputs together check is the whole line, not one coefficient each. (That perturbation was computed here; no published file contains it.)

How large the residual gets across the range. The largest of the four deviations above is at 50 Β°C, which is not an extreme β€” but four points do not bound anything. Sweeping 201 points across the whole declared range [βˆ’50, 150] Β°C with this package's own predict.py, the largest deviation from C + 273.15 is 1.4638900779573305eβˆ’05 at 69 Β°C and the smallest is 5.96eβˆ’09 at βˆ’43 Β°C; at the two edges it is among the smallest (7.21eβˆ’07 at βˆ’50 Β°C, βˆ’2.38eβˆ’08 at 150 Β°C). So the error does not grow towards the edges, and it is about 2.5Γ— the largest figure in the table above. It is float32 rounding noise, not model drift. That sweep was run here and is not published in any file; it is five lines of Python and you can redo it.


2. Use it without MatrixAI

The package is self-usable: predict.py + model.onnx + inference_spec.json, and nothing from MatrixAI. Dependencies are numpy and onnxruntime (requirements.txt).

python -m venv .venv
. .venv/bin/activate            # Windows: .venv\Scripts\activate
pip install -r requirements.txt
python predict.py --input example_input.json
# 323.1500059366226

From your own code:

from predict import MatrixAIModel

model = MatrixAIModel()          # loads inference_spec.json next to predict.py
print(model.predict({"celsius": 50.0}))   # 323.1500059366226

If you plan to verify the package afterwards, do this on a copy. Importing predict.py from inside the bundle writes __pycache__/predict.cpython-3XX.pyc into the package directory, and matrixai verify counts that as a file the manifest does not name: measured, all four stages then come back INCOMPARABLE with exit code 3 (manifest INCOMPARABLE β€” the package ships files the manifest does not cover). Delete it (rm -rf __pycache__) before verifying, or use the package somewhere else. Running python predict.py --input … as a script does not create it; only importing does. That the check notices at all is what makes manifest PASS worth anything.

You feed raw human values (50.0, not 0.5): predict.py applies the same normalization the model was trained with, and puts the answer back on the kelvin scale.

It clips out-of-range inputs, and you have to ask to be told. The declared input range is [βˆ’50, 150] Β°C. Anything outside is clamped to the edge before the model sees it β€” measured by running this package's own predict.py:

{"celsius": 1000}    -> 423.14999997615814     (i.e. 150 Β°C clamped, = 150 + 273.15)
{"celsius": -273.15} -> 223.1500007212162      (i.e. -50 Β°C clamped, = -50 + 273.15)

The plain output does not say it clipped. --meta does β€” this is the literal output, as the command prints it:

python predict.py --input out_of_range.json --meta
{
  "prediction": 423.14999997615814,
  "meta": {
    "spec_version": 1,
    "warnings": [],
    "clipped": [
      {
        "field": "celsius",
        "raw_value": 1000,
        "normalized_value": 1.0
      }
    ]
  }
}

If you wire this into anything, read meta["clipped"]. Those two numbers above are not in salida.txt; they were measured here by running the shipped predict.py, and the clipping itself is in predict.py (_encode_scalar).

For the raw ONNX graph β€” input Reading, shape [-1, 1]; output prediction, shape [-1] (export_manifest.json for the names and shapes; the float32 dtype is from model_manifest.json β†’ inputs[0].dtype) β€” remember it expects an already normalized vector and returns an un-denormalized value. Prefer predict.py.


3. Reproduce the whole thing

Nothing here is a "trust the card" step. All four verification stages are reported below in Β§4 β€” including the one that will not come back PASS on your machine, and why that is the correct answer rather than a fault.

3a. Check this package as it stands

pip install "matrixai-core[export]"
matrixai verify .              # integrity + rebuild the dataset from the recipe
matrixai verify . --retrain    # …and train again (slow)
matrixai verify . --json       # same report, machine readable

Exit codes (the bundle's own README.md): 0 nothing failed Β· 2 something does not match Β· 3 it could not be checked. verify also accepts a .zip directly, and verifying a pristine copy is the right thing to do β€” see the note in Β§2 and the third note in Β§3b.

What you will actually get, measured. Run on 2026-08-30 against the published paquete.zip with matrixai-core 1.7.0 β€” which is what the pip install line above installs today:

$ matrixai verify paquete.zip --retrain --locale en
manifest  PASS
R1        PASS
training  PASS
R3        INCOMPARABLE  β€” the tolerance was measured for the package's own environment (f73756abfe0d07df…) and this one is different (364849887da933a3…), so a difference here would not prove the package wrong
$ echo $?
3

INCOMPARABLE is one of the four verdicts the tool defines (PASS, FAIL, INCOMPARABLE, NOT_RUN) and it means "I could not check this" β€” a different thing from FAIL. Expect it, and expect exit 3. It is not a defect in the package. The second digest will be different again on your machine: it is a digest of your environment.

Pinning the core version will not get you four PASS, and it would be dishonest to suggest it. environment_sha256 covers the matrixai version, the exact CPython build string, the kernel release and the numpy/onnx/onnxruntime/torch versions (matrixai/export/reproduce.py, build_environment). Measured here: recomputing that digest on this machine with only the version string forced to 1.6.0 reproduces f73756abfe0d07df… exactly β€” so on this machine the core version is the single field that moved, and 1.6.0 is still installable from PyPI. On any other machine the CPython build string and the kernel release move as well, and no pin brings those back. R3's tolerance is scoped same_environment_same_seed; when the environment is not the same, the core says so instead of manufacturing a PASS.

The four PASS lines quoted in Β§4 are the literal transcript of the run that built this package, on the machine that built it. They are true of that run. They are not a prediction about yours.

3b. Rebuild it from scratch, from the two source files

model.mxai (the model) and model.mxtrain (the training contract) are in this repository, and they are text you can read in full. Rename them first: model.mxtrain names its model as kelvin.mxai in its first line, so the file has to be called that.

cp model.mxai kelvin.mxai
cp model.mxtrain kelvin.mxtrain
cp data_recipe.txt receta.txt

matrixai generate-dataset kelvin.mxai --training kelvin.mxtrain \
  --rows 300 --seed 20260825 --mode coherent --recipe receta.txt -o datos
matrixai train kelvin.mxai --training kelvin.mxtrain --output runs/v1 \
  --recipe receta.txt --dataset-manifest datos/celsiustokelvin-synthetic-manifest.json
matrixai export-bundle kelvin.mxai --params runs/v1/params.best.json \
  --outdir paquete --training kelvin.mxtrain --data-recipe receta.txt --from-run runs/v1
cd paquete && python3 predict.py --input ../entrada.json
cd .. && zip -r paquete.zip paquete
matrixai verify paquete.zip --retrain

Those are the commands published with the case, plus one that is not: zip. matrixai export-bundle writes a directory and has no zip option (--outdir is the only output flag), so the zip has to be made separately β€” in this project it is made by the build script, not by the CLI. Three more notes:

  • entrada.json is the case's own example input, {"celsius": 0} β€” write that one line, or point --input at example_input.json from this package ({"celsius": 50.0}) and expect 323.1500059366226.
  • --dataset-manifest is how the training run learns the generation seed, the mode and how many rows existed before the split. Without it the package cannot claim its dataset can be regenerated, and it says so instead of pretending.
  • Verify the zip, not the directory you just built. A freshly built bundle directory still has datos/ sitting next to it, and a retrain will quietly use it β€” which is not the thing you wanted to test.

3c. The recipe is the data

predicted_kelvin = 1*celsius + 273.15

That single line (data_recipe.txt, sha256 7f332dbb7aa59ebf7518dba328d62599febc2e693fd528392dee33c43fe729de) is what the 300 generated rows came from β€” 240 of which became the training CSV and 60 the eval CSV (Β§4). It ships inside the package, and verify uses it to regenerate the dataset and compare the digest β€” which is what stage R1 is.

3d. What you need

Python 3.10 or newer (requires-python = ">=3.10") and pip install "matrixai-core[export]". No account, no LLM key, no GPU. It runs on a laptop.


4. Measured numbers, with where each one comes from

Everything below is either copied from a file in this package or was measured by re-running the published files; each row says which. Nothing is rounded for presentation except where the source itself printed it rounded.

Verification β€” matrixai verify paquete.zip --retrain

Literal output of the run that built this package, from salida.txt step 5:

manifest  PASS
R1        PASS
training  PASS
R3        PASS

For what you will get instead, and why, see Β§3a.

Stage What it checks
manifest Every artifact in the package matches the sha256 the manifest declares β€” and nothing travels that the manifest does not name.
R1 The dataset rebuilt from the recipe has the full sha256 the package declares.
training Training runs to completion with what the package carries inside.
R3 The metrics of that fresh run fall inside their declared tolerance.

Each stage reports one of four verdicts β€” PASS, FAIL, INCOMPARABLE ("I could not check this"), NOT_RUN β€” described at matrixaistudio.org/manual/proof.

Model metrics

Metric Value Split Dataset sha256 Source file
RΒ² 1.0 (printed as 1.000000) validation 5827acf2b2b6058d90b6a70d959d882db080bac3ceb6bbe797a801d27201c180 reproduce.json β†’ metrics[0]; also salida.txt step 2
MAE 6.505213034913027e-17 validation same reproduce.json β†’ metrics[1]; salida.txt prints 6.50521e-17
RMSE 9.765943671129598e-17 β€” β€” params.best.json β†’ metrics.rmse
Validation loss 9.537365578767625e-33 validation β€” params.best.json; salida.txt prints 0.000000

Both published metrics declare tolerance_abs: 0.0 and tolerance_scope: "same_environment_same_seed" β€” that is, the tolerance is only claimed for a rerun in the same environment with the same seeds, not for your machine in general.

Both also carry "incomplete": ["evaluator", "evaluator_version", "tolerance_rel"] β€” the package names, itself, the three fields it could not fill.

RΒ² of exactly 1.0 and a MAE of 6.5eβˆ’17 mean the residuals are at the floating-point floor. That is what should happen when the data was generated by a rule the model can represent exactly. See Β§7.

Which rows those metrics are over. The package does not record it (artifacts.dataset.rows_used is null), and a null is an answer, not a zero β€” but it is not a mystery either, and saying only "not recorded" would leave you believing it cannot be known. The 300 generated rows are split into 240 (…-train.csv) + 60 (…-eval.csv) by generate-dataset (salida.txt step 1). model.mxtrain reads only the 240-row train CSV and splits that again train=0.8 validation=0.2 seed=42 (Β§5). Measured by re-running the published commands and reading runs/v1/training_trace.json: rows_train: 192, rows_validation: 48, source: datos/celsiustokelvin-synthetic-train.csv. RΒ² and MAE are over those 48 rows. The 60-row eval CSV is generated and then never used by this pipeline; no metric on it is published.

Data

Rows generated 300 (reproduce.json β†’ artifacts.dataset.rows)
Train / eval split files 240 rows / 60 rows (salida.txt step 1)
Rows the model actually trained on / validated on 192 / 48 β€” measured, runs/v1/training_trace.json of a re-run; the package itself does not record it
Dataset sha256 5827acf2b2b6058d90b6a70d959d882db080bac3ceb6bbe797a801d27201c180
Rows the metric was computed over null (artifacts.dataset.rows_used) β€” not recorded in the package
Seeds dataset 20260825, split 42, init 42
Generation mode coherent
Epochs declared 200 (RUN EPOCHS 200 in model.mxtrain)
Epochs actually run null for both epochs_effective and epochs_ran β€” not recorded in the package. salida.txt reports Best epoch: 25; measured on a re-run, the trace holds all 200 epochs, so all 200 ran and 25 was the best of them
Backend / device stdlib / cpu

ONNX equivalence

From export_manifest.json β†’ equivalence_check:

passed: true   atol: 1e-05   rtol: 0.0001
max_abs_diff: 3.566741946237073e-08
max_rel_diff: 8.65425281073602e-08
n_samples: 20   n_outputs_per_sample: 1

What that actually compares: 20 already-normalized input vectors drawn from np.random.default_rng(seed) with seed = 42 by default β€” the same 20 points on every export, not a fresh sample β€” run through the core's own reference runtime and through onnxruntime, compared on the model's raw output, before predict.py puts it back on the kelvin scale (matrixai/export/equivalence.py, AGPL, in the core repo). Scaled up by the Γ—250 that denormalization applies, 3.57eβˆ’08 is about 8.9eβˆ’06 K β€” that conversion is arithmetic done here, not a published figure. It is a spot check on 20 fixed samples, not a proof of equivalence.

Identity and environment

Project CelsiusToKelvin
Model hash mxai_fdbe5973e98a1123
Parameter schema hash params_e0a02353b5d9884e
Parameter set v1_best
Function kind linear_regression (model_manifest.json)
ONNX opset 17
Exported at 2026-08-26T15:55:05.170951+00:00
Manifest sha256 08f9d835c02574c4d6429063fa4787d08ae6bde432d9de56b3ed9cd97f30f465
Run capture sha256 141ef41bb4b29382561fe264dbdcd1d57e8ca70e7a0f1da8eca5432b251e04e8
Environment sha256 f73756abfe0d07dffabba936f5e66218dd56e5bf3ebc8ee0c507ac69bb3f2f15
Built with matrixai-core 1.6.0, CPython 3.12.3, Linux 6.8.0-137-generic x86_64
Packages numpy 2.4.4, onnx 1.21.0, onnxruntime 1.26.0, torch 2.11.0+cpu

reproduce.json carries a sha256 for 16 of the 17 files that travel, and reports files_covered: 16, missing: [], conflicts: []. Recomputed here: all 16 match. The one it does not cover is itself β€” a manifest cannot carry its own digest inside, so its integrity is declared by manifest_sha256, which is computed by whoever builds the package. What verify does catch, and why manifest PASS is worth something, is any extra file the manifest does not name (see the __pycache__ note in Β§2).

The .mxai, .mxtrain and recipe published at matrixaistudio.org/casos/kelvin/ are byte-identical to model.mxai, model.mxtrain and data_recipe.txt here β€” same three sha256 values, checked with sha256sum.


5. What the model is

The whole model, from model.mxai:

PROJECT CelsiusToKelvin

VECTOR Reading[1]
  celsius: Scalar[-50, 150]
END

PARAM W1 Vector[1]
END

PARAM b1 Scalar
END

FUNCTION PredictedKelvinModel
  predicted_kelvin: Scalar = linear(W1 * Reading + b1)
END

GRAPH
  Reading -> PredictedKelvinModel
END

One weight and one bias. The training contract (model.mxtrain) trains it with MSE and SGD at learning rate 0.5, batch 8, split 80/20 with seed 42, for up to 200 epochs, reading datos/celsiustokelvin-synthetic-train.csv.


6. The two numbers it learned β€” check them by hand

params.best.json holds the entire trained model:

"W1": {"values": [0.8]},
"b1": {"values": 0.0926}

Those look like they have nothing to do with 273.15, and that is only because they live in normalized space. inference_spec.json gives the two ranges, and predict.py applies them: input (C βˆ’ (βˆ’50)) / 200, output y Γ— 250 + 200. Substitute:

K = (0.8 Β· (C + 50) / 200 + 0.0926) Β· 250 + 200
  = 1.0 Β· (C + 50) + 23.15 + 200
  = C + 273.15

In decimal the slope comes out 1 and the offset 273.15 exactly. Stored in float32 they are 0.800000011920929 and 0.09260000288486481, so the trained parameters are the physical constant to within float32 β€” and that "to within" is the whole source of the ~1eβˆ’06 residuals in Β§1. Recomputing the four predictions in float32 from those two numbers alone reproduces all four published outputs digit for digit β€” checked here with numpy, and something you can redo in five lines.

That is the strongest statement this case can make, and it needs no metric.


7. What this does NOT prove

Same weight as everything above.

It does not prove the model is good at anything. It converts degrees. It is a control case: it exists so you can check the machinery, not to solve a problem.

RΒ² = 1.0 is not an achievement. The data was generated from predicted_kelvin = 1*celsius + 273.15, and the model is a single linear unit β€” the exact shape of the rule that made the data. A perfect fit is what should happen. It says nothing about how MatrixAI handles a problem where the answer is not already inside the model's hypothesis class.

The 48 validation rows came out of the same recipe as the training rows. They are held out from the fitting, but not from the rule: there is no data of any other provenance anywhere in this case. RΒ² = 1.0 says nothing about generalizing beyond that one line.

The data is synthetic. No thermometer was involved, no observation of the physical world, no external dataset. Every row came out of that one line.

It says nothing about generalization, and the range is declared, not enforced. Outside [βˆ’50, 150] Β°C the input is clipped to the edge (Β§2), so the package will answer for 1000 Β°C with the answer for 150 Β°C. Nothing here was measured outside the declared range.

Digests are not signatures. reproduce.json states this in its own words:

This manifest proves the package is internally consistent and reproducible: every artifact needed to rebuild this model travels here with its digest, and each one matches the authoritative capture the core recorded while training it. It does NOT prove authorship or authenticity: signatures are out of scope here.

There is no signed receipt in this package. Anybody can change every file and recompute every digest; what the manifest catches is a package that is internally inconsistent, not one that was rebuilt from scratch by someone else.

training PASS means training completed, not that it was good. And R3 only compares a fresh run's metrics against a tolerance the package scopes to same_environment_same_seed. Off the machine that built it, R3 comes back INCOMPARABLE β€” measured, see Β§3a β€” and that is the honest verdict, not a degraded one.

Some numbers in this package are absent, not zero. params.best.json carries accuracy: 0.0, macro_f1: 0.0, macro_precision: 0.0, macro_recall: 0.0. Those are classification fields on a regression model. They are placeholders, not measurements β€” do not read them as "0 % accurate". Likewise rows_used, epochs_effective, epochs_ran, evaluator and evaluator_version are null: the package does not record them, and does not pretend to.

This is not a conformity assessment of anything. Not a certification, not an audit in any regulatory sense, and not evidence for any regulatory regime in any jurisdiction. It is a package that can be rebuilt and checked, which is a different and much smaller claim.

This repository deliberately carries no model-index metrics block. That widget presents a number as a checked result, and an RΒ² of 1.0 on data generated from the model's own hypothesis class is not a result β€” it is arithmetic.

For the case that does not come out clean, see the third one, Will it rain tomorrow?: it fits real observations imperfectly (accuracy 0.762557), and separately it leaves two of the four verification stages INCOMPARABLE and a third NOT_RUN, because its data cannot be regenerated from any recipe. Those are two different limits β€” one of fit, one of verification β€” and it has both.


8. Files in this package

File What it is
model.mxai The model definition (source of truth)
model.mxtrain Training contract: dataset, split, loss, optimizer, epochs
params.best.json The trained weights β€” one weight, one bias
model.onnx ONNX model, opset 17
model_manifest.json Model metadata, hashes, backend contract
export_manifest.json Export metadata, tolerance, equivalence check
data_recipe.txt The rule the training data was generated from
reproduce.json Whether this can be rebuilt, and the digest of every other artifact
inference_spec.json How a raw record maps to the model input
predict.py Standalone wrapper: raw values in, prediction out
requirements.txt numpy + onnxruntime, and nothing else
example_input.json / expected_output.json A runnable example and what it should print
space/ A Hugging Face Space template the exporter emits
README.md The bundle's own readme

Seventeen files; reproduce.json covers the other sixteen.


9. License and links

AGPL-3.0. The model, the package and the MatrixAI core are all AGPL-3.0-only; predict.py carries the SPDX header inside the bundle.

MatrixAI builds, trains, audits and deploys a neural network from a plain-language description or a CSV, and emits a cryptographic receipt at each step. It is open source under AGPL-3.0, and every exported package predicts with no MatrixAI installed.

Built with matrixai-core 1.6.0; the current release is 1.7.0. A different version produces a different environment_sha256 and therefore a different R3 verdict β€” see Β§3a. That is worth knowing rather than hiding.


Make your own

This model is not a demo to look at β€” it is a case you can redo, and the tool that produced it is free.

MatrixAI Studio turns a written description, or a CSV you already have, into a neural network you can question: it builds it, trains it, and emits a cryptographic receipt at every step, so anybody can re-check what you claim. It runs on your own machine β€” no account, no cloud, no API key.

If something on this page is not true, it should be visible from the outside. That is the whole point of publishing the package and not just the numbers.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support