Roadmap - xkqg/DeepSharp GitHub Wiki
This page lists what DeepSharp has built and what it will build, in the order the work is done. What is done comes first, and below it what is still to come.
The one rule behind the order: nothing stands above a row it needs. Among the rows that are free to move, the one a reader reaches for most often, and pays least for, comes first.
There are no dates — a row moves when it is true, and the changelog is where it says so.
The table gives one line per row. The first row says all there is to say; every other row has a section of its own below, under its own name.
| What it is | In one line | |
|---|---|---|
| Done |
Shape, Tensor, ITensorBackend, CpuBackend
|
Everything else is written in terms of these. |
| Done | Pipeline | The artefact PDD is named after: declared once, fitted on train alone, replayed unchanged. |
| Done | Notebook | The same pipeline as a Verso notebook, one block per step. |
| Done |
DeepSharp.Verso.Api and DeepSharp.Verso.Serve
|
The notebook hosted in an application of your own, and DeepSharp's own server. |
| Done | Automatic differentiation | The gradient a network learns by. |
| Done | The layers | Each a forward pass and its gradient, matched against PyTorch on the same rows. |
| Done | Losses and optimizers | The parts that turn a gradient into a change. |
| Done | The training loop | Batches, epochs, validation, early stopping, checkpoints and history — and a network trained behind a pipeline. |
| Done | The two vocabularies | Keras's words or PyTorch's, lowered onto the same model, bit for bit. |
| Done | The charts |
DeepSharp.Charts: what a run keeps, drawn as SVG with MatPlotLibNet. |
| Done | A TorchSharp backend | The same model, its arithmetic on libtorch, on the processor or a graphics card. |
| Done | Loading what already exists | Reading a model trained elsewhere, from PyTorch, Keras and ONNX files. |
| Done | Settling a gap before the features are worked out |
settle.gaps: a value no row decided, written where the features are worked out. |
| Done | A trainer from ML.NET | Boosted trees and a forest, measured beside a network on the same rows. |
| Done | A course to fill in | Every step of a pipeline in its order, waiting for what only a person knows. |
| Done | Live sources | A source that answers differently each time, fetched once and landed as a file. |
| Done | Optimizing | Searching for a network, answers that are a distribution, and what the pipeline owes a network. |
| Done | Everything a window can be | Every convolution and pooling setting that used to be refused, built. |
| Done | Everything the parts can be told | Every layer and optimizer setting that used to be refused, built. |
| Done | What the flock team asked for a third time | Bins, a periodic embedding, how alike the splits are, a better search, averaged weights, a per-key history. |
| Done | The vocabulary a model is written in | The activations, normalisations, losses and measures other libraries write. |
| Done | A model watched after it ships | The rows a served model meets, measured window by window against the training rows its pipeline keeps. |
| Later | Data that is not a table | Images, sound and text read in as rows, varied while training only, and text cut into tokens. |
| Later | Several networks, one answer | Networks trained apart — a study's best, or one design under other seeds — answering together. |
| Later | The answer and what it is worth | Splits that cut more than once, an interval around the answer, and why a model answers as it does. |
| Later | Training further from what was read | A rate of its own for a group of layers, and the low-rank pair beside a frozen layer. |
| Later | A network that is not a straight line | Branches, so a residual block, a U-shaped network or two inputs can be written, read and saved. |
| Later | A row that remembers | The layers that carry something from one step of a series to the next. |
| Later | Attention, and what it attends over | Attention, its many-headed form, the order of a sequence, and one encoder block. |
| Later | Writing what others can read | Writing a model in the graph format every runtime reads. |
| Later | A table of its own | A model that reads a table a column at a time, measured against ML.NET's trees. |
| Later | The engine's own arithmetic | Half-width numbers, a scaled loss and larger batches in the same memory, behind the seam. |
| Later | More than one graphics card | One run spread over several cards, by an engine behind the seam that already does it. |
| Later | Smaller numbers to serve with | A trained model kept in whole numbers of eight bits, so it answers in less memory and time. |
The artefact PDD is named after: collect, settle the gaps, add features, split, name what a model is asked to predict, fill and normalise, and name the network those rows train. It is declared once, fitted on train alone, replayed unchanged on everything after, and the answers are brought back in their own units.
It came first because every later row is written in terms of it, and because the leak it prevents is invisible once it has happened. See Pipeline.
The same pipeline written as a Verso notebook, one block per step:
- the data, a profile and a heatmap at any block;
- its columns chosen from the grid or a list, saved beside it and taken over again.
It runs in Verso's VS Code extension, in verso serve, in DeepSharp's own server and in an application of your own. It
needed only the pipeline, so it did not wait for the rows below. See Notebook.
The notebook's two sister packages:
-
DeepSharp.Verso.Apilets an application of your own host the notebook — opening it, passing a click on, the toolbar and the properties panel, the layouts, the themes and the title, saving, and every view of a notebook told what changed — done for the application rather than by it. Drawing what a block shows stays the application's. -
DeepSharp.Verso.Serveis DeepSharp's own server built on it:deepsharp-serveshows the notebook in a browser and does there what Verso's browser editor does, with nothing else to install.
What the two would share is kept in one place when there is something to share. Like the notebook, they needed only the pipeline.
A network learns by knowing which way to move. Without a gradient there is no training loop, so this is the one piece nothing can be faked around.
- Dense, the activations, dropout, batch and layer normalisation.
- Convolutions along one, two and three axes — in groups, depthwise, dilated, strided on their own for each axis — and the transposed ones that give those places back.
- Max and average poolings, global poolings and the poolings to a size.
- The layers that only pad, the dropouts of whole channels, flattening and reshaping.
Each is a forward pass and its gradient, each starting and working as PyTorch's does, matched against it on the same rows.
The parts that turn a gradient into a change:
- Losses: mean squared error, cross-entropy and binary cross-entropy, each saying how a network's numbers are read, and the earth mover's distance for shares in an order.
- Optimizers: SGD with momentum, Adam, AdamW, RMSprop and Nadam as PyTorch writes them, open to an optimizer of your own.
- A learning-rate schedule: constant, stepped, exponential, a cosine or a warm-up — with gradients clipped by their norm.
- Batches, epochs, validation, and early stopping as Keras's.
- Checkpoints a run goes on from bit for bit on the engine they were taken on.
- The history the charts are drawn from.
Through DeepSharp.Learners.Networks, a network is trained behind a pipeline — declared in that pipeline's own chain and
trained by pipeline.Train(), or written as code and fitted by hand — measured by its report and kept with it as one
file. See Networks.
Stack layers, compile and fit — the words TensorFlow and Keras use — or write the forward pass yourself, the way PyTorch does.
The declarative side is a builder that lowers onto the same model the imperative side produces, so nothing downstream knows which door was used — the same network, bit for bit, and the same file.
Both vocabularies can be spoken in the chain that prepares a pipeline's rows too:
-
.WithTensorflow(…)declares the network those rows train in the words TensorFlow and Keras use; -
.WithTorch(…)declares it in the words PyTorch uses.
pipeline.Train() trains exactly that network, so the model is declared with the pipeline rather than written beside
it. Each writes the number its own library leaves where a caller says none, so the file says which run was asked for.
DeepSharp.Charts, which draws with MatPlotLibNet:
- the loss curve and the learning rate;
- confusion matrices, predicted against actual, and the residuals;
- the measures as bars.
All of it from what the loop and the measures already keep, as SVG, with no viewer to start.
The same model, running its arithmetic on libtorch, on the processor or a graphics card: DeepSharp.Backends.TorchSharp,
TorchBackend.OnCpu() or OnGpu(0), handed to a run and to a trained network as it serves. It is held to the light
engine operation by operation and step by step.
It is a package of its own: a project that does not want it never carries it, the application brings libtorch, and no model changes because of it.
Together with the training loop this is the short version of what the library does — you write it the way TensorFlow taught you, and Torch runs it. See TorchSharp backend.
Reading what was trained elsewhere, so a model does not have to begin here to run here.
The seam is in the core: IImporter, and network.Load, which puts numbers into a network by the path of each slot, all
of them or none. Three packages implement it:
| Package | What it reads |
|---|---|
DeepSharp.Import.PyTorch |
a safetensors file, and the .pt file torch.save writes, its pickle read as PyTorch's weights-only reader reads it |
DeepSharp.Import.Keras |
the .keras and .h5 files Keras saves |
DeepSharp.Import.Onnx |
the graphs PyTorch's exporters, Keras and tf2onnx write |
A model read so is served, measured, or trained further behind a pipeline and kept with it as the one file. See Importing a model.
The problem. A feature is worked out from the columns as they stand, and a fill stands below the split because what it puts in a gap is learned from the training rows. So a sum of two columns one of which has a gap was itself a gap, and filling that column afterwards did not reach back into it.
The answer. settle.gaps is the other half: a nought, a number you choose, or a refusal — none of them a value any
row decided — written where the features are worked out, so a feature after it is worked out from settled columns.
- It learns nothing, so it writes nothing into the fitted half.
- It marks where the gaps were as a fill does.
- It is a verb of its own rather than the fill moved, which is why the pipeline file went to version 6.
The prepared data handed to a trainer from ML.NET, for a table that a tree learns better than a network:
-
.WithML(trainer => trainer.FastTree())names it in the chain. -
pipeline.TrainWithML()runs the pipeline for what it needs —Needs.NoScale, so the scalings a tree does without are left out and written down. - The pipeline's own report then measures the tree and a network on the same rows, which is the only reason the two can honestly be compared.
It names boosted trees and a forest and no other trainer. A declaration promises that running it again gives the same model; measured on this library's rows the stochastic trainers do not keep that promise, while a tree keeps it from the seed the declaration carries.
It is two packages, because the library may not travel where the verb must: ML.NET learner.
A pipeline written from nothing starts with an empty chain and a reader who has to know which verb comes where. A course is every step of a prepared pipeline in the order the steps belong, each present and waiting for what only a person knows — the file to read, the columns, the bounds — and starting everything else as its verb starts it.
There are two, because the rules say a table and a series in time cannot share one order: PipelineCourse.Table and
PipelineCourse.SeriesInTime.
-
A course is a value.
Sayfills in a step,Waitingnames what still waits, andPdd.Fromstarts a pipeline from one filled in all the way. - A course is a file of its own, read through the same door as the pipeline's and under the same version, so no pipeline file moves.
-
In the Notebook, two buttons write every step the blocks do not hold yet, each as the verb's skeleton with
nullwhere only you can say. A block added in DeepSharp's own host arrives as the step that belongs there instead of empty; Verso's own editors never ask a cell type what a cell starts with, so a block added there still starts empty.
A source that answers differently every time it is asked — an exchange's prices, an API — fetched once over a closed
window and landed as a file, which the pipeline then reads like any other, by fingerprint. Each is in a package of its
own, DeepSharp.Pipelines.<Source>, since it brings HTTP, retries and a pacer that a project serving a trained model has
no use for.
DeepSharp.Pipelines.Binance lands the candles of a market on Binance:
await pipeline.ReadBinanceAsync(new BinanceCandles("BTCEUR", "1d", from, to)) lands the window where it is not landed
yet and adds an ordinary .ReadCsv of the file, beside a record of what was asked and what came back.
- Asking again asks Binance nothing.
- A file that was changed since is refused rather than replaced.
- It goes as a guest at an address's shared budget — slowly, waiting once the address has used half of what a minute allows, and stopping at a ban.
- The pipeline's file names the landed file and nothing of Binance, so serving the model never carries the package.
It stands on the readers. See Live sources.
What a network is trained with, searched for and measured by, and what the pipeline owes it:
-
Searching for a network — a study that chooses by the validation rows and keeps the test rows for the winner,
testSeedto fix them, and a paired comparison of two pipelines; - answers that are a distribution in an order, with
emd,klandrps, the earth mover's loss and what a flock that arrived short leaves; -
target.numbers; - two files as one source;
- an id that travels with its rows;
- a correlation worked out once as numbers, Pearson's and Spearman's;
- a run that can be watched and stopped;
- and Writing a step of your own.
What a convolution here covered was neighbouring places, one stride on every axis and the places that fit whole, and a source that said anything else was refused at the setting that said it. Those refusals are built:
- a stride of its own for each axis;
- a dilated window;
- channels in groups, a depthwise and a separable convolution;
- a transposed convolution;
- a border that differs from side to side, and a layer that only pads;
- a pooling that rounds the number of places up, and a pooling to a size other than one place.
Each is held to the proof the rest have — its values and its gradient matched to the framework that has it, on every engine — and each is read from the Keras, ONNX and PyTorch files that say it.
Nothing was added to the tensor seam for any of them: they are the unfolding and its fold, a matrix product, a reshape and the first-largest step, in another order. See Networks and Importing a model.
A layer here took what it needed and little else, and a source that said more was refused at the setting that said it. Those refusals are built.
On the layer's side:
- a layer without a bias, and a normalisation without its scale or its shift;
- nine ways to start a weight or a bias;
- a penalty the loss adds for weights that grow, and a bound on how large a unit's weights may become;
- a layer held still while the rest learns;
- the three borders a padding could not do — the one that mirrors, the one that repeats and the one that wraps.
On the other side of the step:
- the weight decay PyTorch adds to the gradient of Sgd, Adam, RMSprop and Nadam;
- Nesterov's momentum, dampening, AMSGrad, and RMSprop's centred form;
- clipping each gradient value as well as the norm of all of them;
- a step taken once every several batches;
- an average of the weights kept beside them.
Each is held to the proof the rest have — matched, step by step, to the framework that has it, on every engine — and each is read from the Keras, ONNX and PyTorch files that say it. A part that says none of them is written as it always was, so every file an earlier release wrote still reads. Networks says which to reach for when.
A team that weighs flocks wrote for the third time, with what its rows asked of a network here. Each ask is built as this library builds everything: fitted on the training rows alone where it learns, replayed unchanged everywhere after.
- A number written down as how far it fills a few bins whose edges the training rows place.
- The periodic embedding of a number, its frequencies started from a spread the person says, since its authors searched that spread for every dataset.
- A report of how alike training, validation and test are, column by column, that warns and never refuses.
- A search that stops a hopeless trial, draws a setting only when another is chosen, and goes on from the trials it ran before.
- A run judged by the average of its weights, so the best epoch it restores is the best average.
- A running statistic of each key over its earlier training rows, which a single new flock is served with as the training rows were.
What the letter asked for that is refused is refused by name, with why, in the changelog. See Filling and scaling, Searching for a network and Measuring and drawing.
The words the libraries a model is read from write and this library did not:
- Activations: the smooth ones a transformer is made of — GELU, exactly or by its tangent, SiLU and mish — the leaky, parametric and self-normalising ones, and softmax as a layer rather than only inside a loss.
- Normalisations: over groups of channels, over one example's channels, and the one that scales without centring.
- A category as a small vector learned over the columns its encoder writes it down as, which is one layer and no new arithmetic.
- Losses: the absolute error, the one squared near nought and absolute away from it, the one that asks for a quantile rather than a mean, the one that leans on the rare class, the hinge, the one for counts, and cross-entropy with its labels softened.
- Measures beside them: F1, the area under the curve a threshold sweeps out, the error as a share of the answer, and the Brier score with its reliability curve for whether a model that says seventy per cent is right seven times in ten.
It is one release because it was one kind of work done over and over: a forward pass, its gradient, a kind in the file, a read by the three readers — ONNX's both as its newer operators and as the several operators PyTorch's exporters spell each in for older sets — and the framework that has it to hold the numbers to.
The seam gained the sine and the error function for it and nothing more. See Networks, Importing a model and Measuring and drawing.
The likeness of the parts measured how far training, validation and test lie apart, once, while every row was there; and a pipeline read from its file keeps what its steps learned, not the rows they learned it from. So nothing watched the rows a model met once it was served, and a model whose world had moved on went on answering as if it had not.
It is the same measurement turned the other way:
-
watch.reference(.Watch(warnAbove, columns…)), below the split, keeps the training rows of the columns it watches as counts — each value once with how many rows hold it — and changes nothing a replay does; -
prepared.Watched(window)measures a window of served rows against them, column by column, by the same two distances the likeness of the parts takes, flagged above the watch's bound, each beside how many rows it was worked out from, and says when the window was; - nothing is learned from the served rows, so the reference stays the one the model was fitted on, and the windows are the
caller's —
DeepSharp.Chartsdraws them one beside another.
A model trained before its pipeline kept the rows is watched once both are made again. See Measuring and drawing.
There are convolutions over images and volumes, embeddings and the activations a transformer is made of, and still every row has to arrive as a table: whoever has a folder of pictures turns the pixels into columns by hand first.
What it takes:
- a source that reads images, sound and text as rows of numbers, beside the files a pipeline reads today;
- a variation of the training rows only — a flip, a crop, noise — declared in the pipeline and drawn from a seed of its own, so validation and test are never varied and a run can be replayed;
- a tokenizer that cuts text into the ids an embedding takes, learned from the training rows alone.
It stands here because the layers are built and the way in is not, and because it is what a reader with anything but a table meets first.
A study tries many networks and keeps one; a comparison says whether a change helped. Neither lets several trained networks answer together, which is often steadier than any one of them. The average a run keeps is of one network's weights over its own steps, not of several networks' answers.
What it takes:
- networks trained apart on the rows one pipeline prepared — the best few of a study, or one design under other seeds;
- their answers averaged, or for classes their chances averaged or their votes counted, in the answer's own units;
- measured by the same report on the same rows as each of its members, so what the ensemble adds is a number;
- kept as one file with the pipeline, refusing any other fit of it, as a single network is.
It stands here because a study and its trained networks are built, and the rest is the answer they give together.
Three things that are one piece of work — they all touch the split, the report and nothing else — and they are what a reader of this library reaches for most often after a model trains at all:
- Splits that cut more than once. Every split here cuts once — at random, by time, or keeping the classes' shares — so a small table is measured by luck and a series is not measured the way forecasting is, by moving the cut forward and fitting again.
- A bound. Rows set aside before anything is fitted, what is worked out from them worked out once, and the answer handed back with an interval around it in its own units — which is this library's own shape rather than a new idea in it.
- Why. The third thing a reader asks the moment an answer appears: which column the answer leans on, what happens to it as one column moves through its range, and what a single row's answer is made of.
The quantile loss it needs is built.
A model read from elsewhere can be trained on further here, and with no say over what moves: everything moves, at one rate, all of it learning.
The held-still layer is built. What it still needs:
- a rate of its own for a group of layers;
- the small pair of low-rank matrices beside a frozen layer that has become how a large layer is adapted — which a reader will meet in a Keras file sooner or later anyway, since that is where it is written.
It stands here because reading a model is built already, the rest of it is small, and nothing below is needed for any of it.
Everything here is a line: layers in order, each handed what the one before it made. A reader meeting anything else refuses it by name — a branch, a node taking another value than the one the node before it made, or two. That one refusal is why a residual block, a dense block, a network shaped like a U, a cascade whose every added unit sees every earlier one, and anything with two inputs can be neither written, read nor saved here.
What it takes:
- a step that may ask for the output of a step other than the last;
- an add and a joining of what two steps made;
- a name per step for the asking to be written down;
- a shape in the file for a network that is not a sequence.
The readers then stop refusing a branch, and a model trained elsewhere arrives as it was.
It is the hardest row on this list and the one most of what people train today needs, which is why it is here and not later. It needs the row that can hold a layer still, because a cascade that grows is only honest when what it added stays put, and every row below it waits for it.
The pipeline prepares a series — a course of its own for it, a feature for what comes round again, the indicators — and nothing on the network side carries anything from one step of that series to the next.
What it takes:
- the two layers that do, as every library writes them;
- a batch whose rows are of unequal length, and the mask that says where each ends;
- the two wrappers that make them usable: one that reads the series both ways, one that applies a layer to every step.
The gradient of a network that remembers runs back through time, which is new ground for the backend that records one. What these layers hold inside them is not a line either, so it waits for the row above.
The one step everything since has been built from: attention, its many-headed form, somewhere to put the order of a sequence, and one encoder block that puts them together. A mask that hides what a step may not look at is what it gains, softmax over the last axis being a layer already.
It waits for a network that may branch, since an attention block is a branch twice over. It is here rather than higher because almost nothing a reader of this library prepares today asks for it before that row exists.
Three readers are built and no writer. A model trained here runs here, and nowhere else — while the graph format every runtime reads is one this library already parses, with the classes for it already in hand.
Writing one back is the symmetric half of a reader that works:
- a kind for every node;
- the two version numbers a graph carries;
- its own proof — write it, run it where it is meant to run, and hold the numbers to ours.
Serving on that runtime is a smaller row beside it, and a different one: a runtime that only answers is not an engine this library can train on, and saying so is half of that design.
It waits for the row that lets a network branch, since a writer that can only write a line would refuse the models anyone most wants out.
The library's own ground is tables, and the shape that reads a table as a model of this kind — a column at a time, attended over — is the one piece of the current state of the art that is missing here.
It is built to be measured, not to be claimed: against the trees from ML.NET, on the same rows, through the pipeline's own report, which is the only comparison that means anything. What it does not do is carry somebody else's trained weights: a model that arrives already knowing something is a decision about what this library ships, not a layer.
It waits for the three rows above it, and it is the one row here that could turn out not to be worth its own weight, which is why it is not higher.
- Numbers kept in half the width, with a full-width copy to be updated.
- The loss scaled so small gradients survive.
- A sum of gradients kept wider than the batches that made it.
- Layers whose workings are thrown away and worked out again to fit a larger batch in the same memory.
All of it belongs behind the seam, on the engine that already has it, and none of it is hand-written GPU code — which is not on this list and does not become so here. It is last because nothing above it needs it and nobody meets it until a run is large enough to be slow.
One run spread over several cards — each a share of the batch, the gradients added together. It belongs behind the seam, on an engine that already does it, as the GPU training host this library may reach does; nothing of it is written here. It is late because one card answers every run this library sees today.
A trained model kept in whole numbers of eight bits rather than in single precision, so it answers in less memory and time — measured, as everything here, against the answers it gave before. It asks for arithmetic the seam does not have, so it comes after the rows that use the seam as it is.
Reaching a GPU means using an engine that already does, through the seam. No CUDA is written here, and the GPU training host it would reach stays a separate project.
No description of a network kept beside the model and held in step with it, however convenient it would be for one feature. Two of those diverge on the first unusual model, and then every bug is asked twice.
A network that branches is not that and is on the list above: a step may come to ask for what a step other than the last one made, and it does so inside the one model every engine runs and every file holds.
Nor is a file written out for another runtime to read: that is written from the model and never read back as the model.
Names that are clear in C# beat names that are familiar from Python. Where the two agree, the familiar one wins; where they do not, the reader here comes first.