Repository navigation
Roadmap
What is done comes first, and below it what is still to come. 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. No dates — a row moves when it is true, and the changelog is where it says so.
| What it is | Why it comes here | |
|---|---|---|
| Done |
Shape, Tensor, ITensorBackend, CpuBackend
|
Everything else is written in terms of these. |
| Done | Pipeline | 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 — declared once, fitted on train alone, replayed unchanged on everything after, and the answers 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. |
| Done | Notebook | The same pipeline written as a Verso notebook, one block per step, with the data, a profile and a heatmap at any block, and 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. |
| Done |
DeepSharp.Verso.Api and DeepSharp.Verso.Serve
|
The notebook's two sister packages. DeepSharp.Verso.Api lets 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.Serve is DeepSharp's own server built on it: deepsharp-serve shows 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. |
| Done | Automatic differentiation | 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. |
| Done | The layers | 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 a forward pass and its gradient, each starting and working as PyTorch's does, matched against it on the same rows. |
| Done | Losses and optimizers | 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; SGD with momentum, Adam, AdamW, RMSprop and Nadam as PyTorch writes them, open to an optimizer of your own; and a learning-rate schedule — constant, stepped, exponential, a cosine or a warm-up — with gradients clipped by their norm. The parts that turn a gradient into a change. |
| Done | The training loop | Batches, epochs, validation, early stopping as Keras's, checkpoints a run goes on from bit for bit on the engine they were taken on, and the history the charts are drawn from — and, through DeepSharp.Learners.Networks, a network 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. |
| Done | The two vocabularies | 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 and .WithTorch(…) in the words PyTorch uses, and 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. |
| Done | The charts |
DeepSharp.Charts, which draws with MatPlotLibNet: the loss curve, the learning rate, confusion matrices, predicted against actual, the residuals and the measures as bars — all from what the loop and the measures already keep, as SVG, with no viewer to start. |
| Done | A TorchSharp backend | 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, held to the light engine operation by operation and step by step. Its own package: 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. |
| Done | Loading what already exists | 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 — and three packages implement it: DeepSharp.Import.PyTorch for a safetensors file and the .pt file torch.save writes, its pickle read as PyTorch's weights-only reader reads it; DeepSharp.Import.Keras for the .keras and .h5 files Keras saves; DeepSharp.Import.Onnx for 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. |
| Done | Settling a gap before the features are worked out | 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. 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; and it is a verb of its own rather than the fill moved, which is why the pipeline file went to version 6. |
| Done | A trainer from ML.NET | 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 and pipeline.TrainWithML() runs the pipeline for what it needs — Needs.NoScale, so the scalings a tree does without are left out and written down — and 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, and 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. |
| Done | A course to fill in | 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 — Say fills in a step, Waiting names what still waits, and Pdd.From starts a pipeline from one filled in all the way — and 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 null where only you can say, and 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. See Starting from a course. |
| Done | Live sources | 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 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, and 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 — and 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. |
| Done | Optimizing | 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, testSeed to fix them, and a paired comparison of two pipelines; answers that are a distribution in an order, with emd, kl and rps, 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. |
| Done | Everything a window can be | 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, 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 — each is read from the Keras, ONNX and PyTorch files that say it, and 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. |
| Done | Everything the parts can be told | 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: a layer without a bias, 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, a bound on how large a unit's weights may become, a layer held still while the rest learns, and the three borders a padding could not do — the one that mirrors, the one that repeats and the one that wraps. The same 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, RMSprop's centred form, clipping each gradient value as well as the norm of all of them, a step taken once every several batches, and 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 — each is read from the Keras, ONNX and PyTorch files that say it, and 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. |
| Later | The vocabulary a model is written in | Three activations are built where the libraries a model is read from write a dozen: the smooth ones a transformer is made of, the leaky and parametric ones, the self-normalising one, and softmax as a layer rather than only inside a loss. Two normalisations are built where there are four — over groups of channels, over one example's channels, and the one that scales without centring. A category becomes columns of noughts and ones where a model of this kind wants a small learned vector for it, which is one layer and no new arithmetic. And four losses are built where a table and a series ask for more: the absolute error, the one that is 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 — with the measures beside them, the harmonic mean of precision and recall, the area under the curve a threshold sweeps out, the error as a share of the answer, and whether a model that says seventy per cent is right seven times in ten. One release rather than three, because it is one kind of work done over and over: a forward pass, its gradient, a kind in the file, a read by the three readers, and the framework that has it to hold the numbers to. It needs the row above for the number a parametric activation learns. |
| Later | The answer and what it is worth | 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 is the other half: 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. And the third thing a reader asks the moment an answer appears is why: 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. 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. It needs the quantile loss from the row above. |
| Later | Training further from what was read | 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 is a rate of its own for a group of layers, and the small pair of low-rank matrices beside a frozen one 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. |
| Later | A network that is not a straight line | Everything here is a line: layers in order, each handed what the one before it made, and 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, and 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. |
| Later | A row that remembers | 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. 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, and 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, and what these layers hold inside them is not a line either, so it waits for the row above. |
| Later | Attention, and what it attends over | 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. Softmax over an axis and a mask that hides what a step may not look at are what the seam gains. It waits for the vocabulary and for a network that may branch, since an attention block is a branch twice over, and it is here rather than higher because almost nothing a reader of this library prepares today asks for it before those two rows exist. |
| Later | Writing what others can read | 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, and 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. |
| Later | A table of its own | 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. |
| Later | The engine's own arithmetic | 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, and 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. |
Hand-written GPU code. 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.
A second network representation. 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.
Matching a Python API call for call. 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.