Documentation
¶
Overview ¶
Package goml runs trained machine-learning models in pure Go, with output that matches the original scikit-learn estimator.
Caution: this library is fully AI-generated ¶
The implementation, tests, examples and documentation were all written by an AI agent.
Every shipped model is checked against scikit-learn's own outputs by the test suite in the repository, but the only real-world validation is against production workloads specific to Heru, Inc. Anything those workloads do not exercise — other estimator types, other hyper-parameters, other scikit-learn versions, other input regimes — is unproven, and exactness against a moving upstream is exactly where that gap is most likely to hurt. Use it with caution: read the code, validate your own exported model against your own scikit-learn outputs, and treat the fidelity claims below as claims to verify rather than as guarantees.
Goals ¶
The package is built around three ideas:
- Static, not dynamic. A model is exported once from Python (see tools/sklexport) into a portable go-ml/v1 JSON document. That document is either loaded at startup or, for maximum speed and zero runtime dependencies, compiled directly into your binary as Go source with the go-ml-gen tool (see cmd/go-ml-gen). There is no Python, no pickle, and no model file at runtime.
- Generic. Models are values behind small interfaces (Model, Classifier, Regressor). New estimator types register a decoder with Register and are then usable through the same Load entry point, mirroring how image.RegisterFormat or database/sql drivers work. The models that ship today are RandomForestClassifier and ExtraTreesClassifier, both in package github.com/heru-opensource/go-ml/ensemble.
- Faithful. Each model reproduces scikit-learn's prediction path exactly, down to its internal float32 cast and missing-value handling, so Go and Python agree to within floating-point rounding.
Loading a model ¶
Import the package that implements your model type for its side-effect registration, then load by type from the export envelope:
import (
goml "github.com/heru-opensource/go-ml"
_ "github.com/heru-opensource/go-ml/ensemble" // registers the forest models
)
clf, err := goml.LoadClassifierFile("model.json")
if err != nil { ... }
proba, err := clf.PredictProba([][]float64{{1, 2, 3, ...}})
The API mirrors scikit-learn: Classifier.PredictProba returns per-class probabilities in Classifier.Classes order, and Classifier.Predict returns the arg-max class label.
Missing features ¶
Tree models handle missing values natively. Pass math.NaN for an absent feature; it is routed exactly as scikit-learn would route it.
Bundles ¶
A deployed model is often not one estimator: a decision may take two or three of them plus the thresholds they were tuned against, and those thresholds are as much a fitted parameter as any split in a tree. LoadBundle reads a go-ml/bundle-v1 document holding all of it, Bundle.Classifier returns a member by name, and the metadata accessors return the tuned values typed:
b, err := goml.LoadBundleFile("cascade.json")
screen, err := b.Classifier("screen")
threshold, err := b.Float("screen_confidence") // missing key: an error, not a zero
go-ml carries metadata and hands it back; it does not interpret it.
Feature names ¶
The order of a feature vector is part of a model, and a vector assembled in the wrong order is made of individually valid numbers — no validation can catch it. When scikit-learn recorded feature_names_in_ (which it does for an estimator fitted on a named frame), the export carries those names, Model.FeatureNames returns them in column order, and Assembler builds inputs from a name-keyed map:
a, err := goml.NewAssembler(clf) // ErrNoFeatureNames if the export has none
row, err := a.Row(map[string]float64{"petal_length": 1.4, "sepal_width": 3.5})
An unknown name is an error; a name the model expects but the map omits is a missing feature. A retrain that reorders or renames features then changes the export rather than silently breaking its callers.
Example ¶
Example loads an exported model and predicts with it. The API mirrors scikit-learn: PredictProba returns per-class probabilities in Classes order, and Predict returns the arg-max label.
package main
import (
"fmt"
"log"
goml "github.com/heru-opensource/go-ml"
_ "github.com/heru-opensource/go-ml/ensemble"
)
// miniExport is a one-tree RandomForestClassifier over a single feature: class
// 0 when feature 0 is <= 0.5, class 1 above it. Real exports are produced from
// a fitted scikit-learn estimator by tools/sklexport; this one is small enough
// to read inline.
const miniExport = `{
"format": "go-ml/v1",
"type": "RandomForestClassifier",
"model": {
"n_features": 1, "n_outputs": 1, "classes": [0, 1],
"trees": [{
"node_count": 3, "value_width": 2,
"left": [1, -1, -1], "right": [2, -1, -1], "feature": [0, -2, -2],
"threshold": [0.5, -2, -2], "missing_left": [false, false, false],
"value": [0.5, 0.5, 1, 0, 0, 1]
}]
}
}`
func main() {
clf, err := goml.LoadClassifierBytes([]byte(miniExport))
if err != nil {
log.Fatal(err)
}
X := [][]float64{{0.2}, {0.9}}
proba, err := clf.PredictProba(X)
if err != nil {
log.Fatal(err)
}
labels, _ := clf.Predict(X)
fmt.Println(clf.Type(), clf.NFeatures(), clf.Classes())
fmt.Printf("proba = %.1f\n", proba)
fmt.Printf("labels = %v\n", labels)
}
Output: RandomForestClassifier 1 [0 1] proba = [[1.0 0.0] [0.0 1.0]] labels = [0 1]
Example (CustomModel) ¶
Example_customModel loads an export of the type registered above. Nothing in the loading code knows about dummyClassifier: goml.Load dispatches on the envelope's "type", exactly as image.Decode dispatches on a magic number.
package main
// This file is one whole-file example: how a new scikit-learn estimator type
// plugs into go-ml. It implements sklearn.dummy.DummyClassifier's "prior"
// strategy — the baseline that ignores its input and always returns the class
// distribution it was fitted on — because that keeps the prediction path down
// to a few lines. A real model does more arithmetic, and nothing else changes.
//
// go-ml does not ship a DummyClassifier; the ensemble package registers its
// forests exactly this way.
import (
"encoding/json"
"fmt"
"log"
goml "github.com/heru-opensource/go-ml"
)
// dummyClassifier predicts fixed class priors, whatever the input.
type dummyClassifier struct {
nFeatures int
classes []float64
featureNames []string
prior []float64
}
func (d *dummyClassifier) Type() string { return "DummyClassifier" }
func (d *dummyClassifier) NFeatures() int { return d.nFeatures }
func (d *dummyClassifier) Classes() []float64 { return append([]float64(nil), d.classes...) }
// FeatureNames completes goml.Model. Returning nil is allowed and means the
// export carried no names; passing them through, as here, is what lets callers
// build inputs with a goml.Assembler instead of by position.
func (d *dummyClassifier) FeatureNames() []string {
return append([]string(nil), d.featureNames...)
}
// PredictProba returns the prior for every sample. Validating the feature count
// (and nothing else) is the convention every model here follows.
func (d *dummyClassifier) PredictProba(X [][]float64) ([][]float64, error) {
out := make([][]float64, len(X))
for i, x := range X {
if len(x) != d.nFeatures {
return nil, fmt.Errorf("%w: sample %d has %d, want %d",
goml.ErrNumFeatures, i, len(x), d.nFeatures)
}
out[i] = append([]float64(nil), d.prior...)
}
return out, nil
}
func (d *dummyClassifier) Predict(X [][]float64) ([]float64, error) {
proba, err := d.PredictProba(X)
if err != nil {
return nil, err
}
out := make([]float64, len(proba))
for i, p := range proba {
best := 0
for c := range p {
if p[c] > p[best] {
best = c
}
}
out[i] = d.classes[best]
}
return out, nil
}
// decodeDummy builds the model from the type-specific "model" object of an
// export envelope. The exporter on the Python side (see tools/sklexport) writes
// the matching JSON.
func decodeDummy(raw json.RawMessage) (goml.Model, error) {
var j struct {
NFeatures int `json:"n_features"`
Classes []float64 `json:"classes"`
FeatureNames []string `json:"feature_names"`
Prior []float64 `json:"prior"`
}
if err := json.Unmarshal(raw, &j); err != nil {
return nil, err
}
if len(j.Classes) != len(j.Prior) {
return nil, fmt.Errorf("dummy: %d classes but %d priors", len(j.Classes), len(j.Prior))
}
if n := len(j.FeatureNames); n > 0 && n != j.NFeatures {
return nil, fmt.Errorf("dummy: %d feature names for %d features", n, j.NFeatures)
}
return &dummyClassifier{
nFeatures: j.NFeatures,
classes: j.Classes,
featureNames: j.FeatureNames,
prior: j.Prior,
}, nil
}
// Registration is global and permanent, so it belongs in an init function —
// which is why importing a model package for its side effect is all a caller
// has to do.
func init() {
goml.Register("DummyClassifier", decodeDummy)
}
// Example_customModel loads an export of the type registered above. Nothing in
// the loading code knows about dummyClassifier: goml.Load dispatches on the
// envelope's "type", exactly as image.Decode dispatches on a magic number.
func main() {
const export = `{
"format": "go-ml/v1",
"type": "DummyClassifier",
"model": {
"n_features": 4, "classes": [0, 1, 2], "prior": [0.6, 0.3, 0.1],
"feature_names": ["sepal_length", "sepal_width", "petal_length", "petal_width"]
}
}`
clf, err := goml.LoadClassifierBytes([]byte(export))
if err != nil {
log.Fatal(err)
}
proba, _ := clf.PredictProba([][]float64{{5.1, 3.5, 1.4, 0.2}})
labels, _ := clf.Predict([][]float64{{5.1, 3.5, 1.4, 0.2}})
fmt.Println(clf.Type(), clf.NFeatures(), clf.Classes())
fmt.Println(clf.FeatureNames())
fmt.Printf("proba = %.1f label = %v\n", proba[0], labels[0])
}
Output: DummyClassifier 4 [0 1 2] [sepal_length sepal_width petal_length petal_width] proba = [0.6 0.3 0.1] label = 0
Example (Errors) ¶
Example_errors shows the two failures worth handling explicitly. Both are sentinel errors, so test them with errors.Is rather than by string.
package main
import (
"errors"
"fmt"
goml "github.com/heru-opensource/go-ml"
_ "github.com/heru-opensource/go-ml/ensemble"
)
// miniExport is a one-tree RandomForestClassifier over a single feature: class
// 0 when feature 0 is <= 0.5, class 1 above it. Real exports are produced from
// a fitted scikit-learn estimator by tools/sklexport; this one is small enough
// to read inline.
const miniExport = `{
"format": "go-ml/v1",
"type": "RandomForestClassifier",
"model": {
"n_features": 1, "n_outputs": 1, "classes": [0, 1],
"trees": [{
"node_count": 3, "value_width": 2,
"left": [1, -1, -1], "right": [2, -1, -1], "feature": [0, -2, -2],
"threshold": [0.5, -2, -2], "missing_left": [false, false, false],
"value": [0.5, 0.5, 1, 0, 0, 1]
}]
}
}`
func main() {
// An estimator type nobody registered — usually a missing import of the
// package that implements it.
_, err := goml.LoadBytes([]byte(`{"format":"go-ml/v1","type":"SomeFutureModel","model":{}}`))
fmt.Println("unknown type:", errors.Is(err, goml.ErrUnknownType))
// A sample whose length does not match the model. This is the only
// per-call validation go-ml does.
clf, _ := goml.LoadClassifierBytes([]byte(miniExport))
_, err = clf.PredictProba([][]float64{{1.0, 2.0}})
fmt.Println("wrong feature count:", errors.Is(err, goml.ErrNumFeatures))
}
Output: unknown type: true wrong feature count: true
Example (MissingFeatures) ¶
Example_missingFeatures shows the missing-value convention: an absent feature is math.NaN, and the trees route it the way scikit-learn learned to during fitting rather than erroring or imputing. Here the root's missing direction is right, which is the class-1 leaf.
package main
import (
"fmt"
"log"
"math"
goml "github.com/heru-opensource/go-ml"
_ "github.com/heru-opensource/go-ml/ensemble"
)
// miniExport is a one-tree RandomForestClassifier over a single feature: class
// 0 when feature 0 is <= 0.5, class 1 above it. Real exports are produced from
// a fitted scikit-learn estimator by tools/sklexport; this one is small enough
// to read inline.
const miniExport = `{
"format": "go-ml/v1",
"type": "RandomForestClassifier",
"model": {
"n_features": 1, "n_outputs": 1, "classes": [0, 1],
"trees": [{
"node_count": 3, "value_width": 2,
"left": [1, -1, -1], "right": [2, -1, -1], "feature": [0, -2, -2],
"threshold": [0.5, -2, -2], "missing_left": [false, false, false],
"value": [0.5, 0.5, 1, 0, 0, 1]
}]
}
}`
func main() {
clf, err := goml.LoadClassifierBytes([]byte(miniExport))
if err != nil {
log.Fatal(err)
}
proba, err := clf.PredictProba([][]float64{{math.NaN()}})
if err != nil {
log.Fatal(err)
}
fmt.Printf("%.1f\n", proba[0])
}
Output: [0.0 1.0]
Index ¶
- Constants
- Variables
- func Register(typeName string, dec Decoder)
- func RegisteredTypes() []string
- type Assembler
- type Bundle
- func (b *Bundle) Bool(key string) (bool, error)
- func (b *Bundle) Classifier(name string) (Classifier, error)
- func (b *Bundle) Float(key string) (float64, error)
- func (b *Bundle) Int(key string) (int, error)
- func (b *Bundle) Meta(key string, v any) error
- func (b *Bundle) MetaKeys() []string
- func (b *Bundle) Model(name string) (Model, error)
- func (b *Bundle) Names() []string
- func (b *Bundle) String(key string) (string, error)
- type Classifier
- type Decoder
- type Model
- type Regressor
Examples ¶
Constants ¶
const BundleFormat = "go-ml/bundle-v1"
BundleFormat is the envelope version LoadBundle reads.
const Format = "go-ml/v1"
Format is the export envelope version this package reads.
Variables ¶
var ( // ErrUnknownModel is returned by [Bundle.Model] and [Bundle.Classifier] for // a name the bundle does not contain. ErrUnknownModel = errors.New("goml: bundle has no such model") // ErrUnknownMeta is returned by the [Bundle] metadata accessors for a key // the bundle does not carry. ErrUnknownMeta = errors.New("goml: bundle has no such metadata key") )
Errors returned by the bundle loaders and accessors. Use errors.Is to test.
var ( // ErrFormat is returned when the export envelope has an unsupported format. ErrFormat = errors.New("goml: unsupported export format") // ErrUnknownType is returned by Load when no decoder is registered for the // model's type. Importing the package that implements the model (for its // side-effect Register call) fixes this. ErrUnknownType = errors.New("goml: unknown model type") // ErrNotClassifier is returned by the classifier loaders when the loaded // model does not implement Classifier. ErrNotClassifier = errors.New("goml: model is not a Classifier") // ErrNumFeatures is returned by a model's predict methods when an input // sample has the wrong number of features. ErrNumFeatures = errors.New("goml: wrong number of features") )
Errors returned by the loading functions. Use errors.Is to test for them.
var ErrNoFeatureNames = errors.New("goml: model carries no feature names")
ErrNoFeatureNames is returned by NewAssembler when the model does not know its feature names — scikit-learn records them only for an estimator fitted on a named frame. Use errors.Is to test for it.
var ErrUnknownFeature = errors.New("goml: unknown feature")
ErrUnknownFeature is returned by Assembler.Row for a name the model does not have. Use errors.Is to test for it.
Functions ¶
func Register ¶
Register installs a decoder for a model type (the value of the envelope's "type" field, e.g. "RandomForestClassifier"). It is normally called from a model package's init function. Register panics if dec is nil or if typeName is already registered, mirroring image.RegisterFormat and sql.Register.
func RegisteredTypes ¶
func RegisteredTypes() []string
RegisteredTypes returns the sorted list of model types that have a decoder.
Types ¶
type Assembler ¶ added in v0.2.0
type Assembler struct {
// contains filtered or unexported fields
}
An Assembler builds feature vectors in a model's own column order.
It exists because order is part of a model: a vector assembled in the wrong order is made entirely of individually valid numbers, so no amount of validation downstream can catch it — the model simply predicts confidently from nonsense. Passing names instead of positions moves that failure from silent to impossible, and it survives a retrain that reorders or renames features, because the names travel with the export.
An Assembler is read-only after construction and safe for concurrent use.
func NewAssembler ¶ added in v0.2.0
NewAssembler returns an Assembler for m's feature names, or ErrNoFeatureNames if the export carried none. It also rejects duplicate names, which would make a name ambiguous.
Example ¶
ExampleNewAssembler builds inputs by name instead of by position.
Feature order is part of a model, and a vector in the wrong order is made of individually valid numbers — nothing downstream can catch it. When the export carries names (scikit-learn records them for an estimator fitted on a named frame), assembling by name makes that mistake impossible, and a retrain that reorders columns changes the export rather than silently breaking callers.
package main
import (
"errors"
"fmt"
"log"
goml "github.com/heru-opensource/go-ml"
_ "github.com/heru-opensource/go-ml/ensemble"
)
func main() {
clf, err := goml.LoadClassifierFile("testdata/models/iris.json")
if err != nil {
log.Fatal(err)
}
fmt.Println(clf.FeatureNames())
a, err := goml.NewAssembler(clf)
if err != nil {
log.Fatal(err) // the export carries no names
}
// Order here is deliberately not the model's, and petal_width is absent —
// an omitted feature is a missing one (NaN), which the trees route natively.
row, err := a.Row(map[string]float64{
"petal_length": 1.4,
"sepal_width": 3.5,
"sepal_length": 5.1,
})
if err != nil {
log.Fatal(err)
}
fmt.Println(a.Missing(map[string]float64{"petal_length": 1.4, "sepal_width": 3.5, "sepal_length": 5.1}))
label, _ := clf.Predict([][]float64{row})
fmt.Printf("class %g\n", label[0])
// A name the model does not have is an error, not a silently dropped value.
_, err = a.Row(map[string]float64{"petal_len": 1.4})
fmt.Println(errors.Is(err, goml.ErrUnknownFeature))
}
Output: [sepal_length sepal_width petal_length petal_width] [petal_width] class 0 true
func (*Assembler) Missing ¶ added in v0.2.0
Missing returns the names the model expects that values does not supply, in column order. Assembler.Row treats these as missing features; call this first when a missing input should be rejected instead, or logged.
func (*Assembler) Names ¶ added in v0.2.0
Names returns a copy of the feature names, in column order.
func (*Assembler) Row ¶ added in v0.2.0
Row builds one feature vector from named values.
A name the model does not have is an error wrapping ErrUnknownFeature — a typo or a stale caller is exactly what this type exists to catch. A name the model does have but values omits is math.NaN, the package's missing-feature convention, which tree models route natively.
type Bundle ¶ added in v0.2.0
type Bundle struct {
// contains filtered or unexported fields
}
A Bundle is several named models shipped as one artifact, together with the scalars that were tuned alongside them.
It exists because a deployed model is often not one estimator. A decision may need two or three of them plus the thresholds they were tuned against — and those thresholds are as much a fitted parameter as any split in a tree. Keeping them in hand-written Go beside the model is what goes stale: the numbers and the trees are updated by different hands, at different times, and nothing fails until the predictions are quietly wrong. A bundle makes them one file, versioned and deployed together.
What a bundle deliberately does not do is interpret its metadata. go-ml carries the numbers and hands them back typed; what a threshold means is the caller's own logic, and a missing key is a loud error rather than a zero.
A Bundle is read-only after loading and safe for concurrent use.
func LoadBundle ¶ added in v0.2.0
LoadBundle reads a go-ml/bundle-v1 document from r and decodes every model in it, dispatching each on its own "type" exactly as Load does.
func LoadBundleBytes ¶ added in v0.2.0
LoadBundleBytes is LoadBundle on an in-memory document, for pairing with //go:embed.
Example ¶
ExampleLoadBundleBytes loads several models shipped as one artifact, together with the scalars tuned alongside them.
A deployed model is often not one estimator: a decision may take two of them plus the thresholds they were tuned against, and those thresholds are as much a fitted parameter as any split in a tree. Keeping them in hand-written code beside the model is what goes stale — so a bundle carries them, and a key that is not there is an error rather than a zero.
b, err := goml.LoadBundleBytes([]byte(miniBundle))
if err != nil {
log.Fatal(err)
}
fmt.Println("models: ", b.Names())
fmt.Println("metadata:", b.MetaKeys())
screen, err := b.Classifier("screen")
if err != nil {
log.Fatal(err)
}
threshold, err := b.Float("threshold")
if err != nil {
log.Fatal(err)
}
proba, _ := screen.PredictProba([][]float64{{0.9}})
fmt.Printf("p=%.2f >= %.2f: %v\n", proba[0][1], threshold, proba[0][1] >= threshold)
_, err = b.Float("threshold_v2")
fmt.Println("missing key is an error:", errors.Is(err, goml.ErrUnknownMeta))
Output: models: [confirm screen] metadata: [cutoffs enabled max_stages threshold tuned_for unbounded] p=1.00 >= 0.83: true missing key is an error: true
func LoadBundleFile ¶ added in v0.2.0
LoadBundleFile loads a bundle from a file.
func NewBundle ¶ added in v0.2.0
NewBundle assembles a bundle from already-built models and raw JSON metadata values. This is the constructor statically generated code calls (see cmd/go-ml-gen); most callers load a bundle instead.
func (*Bundle) Classifier ¶ added in v0.2.0
func (b *Bundle) Classifier(name string) (Classifier, error)
Classifier is Bundle.Model followed by a Classifier assertion.
func (*Bundle) Float ¶ added in v0.2.0
Float returns a numeric metadata value. The non-finite sentinels the export format uses ("Infinity", "NaN") decode here too.
func (*Bundle) Int ¶ added in v0.2.0
Int returns an integer metadata value, rejecting a number with a fractional part rather than truncating it.
func (*Bundle) Meta ¶ added in v0.2.0
Meta decodes a metadata value into v, for anything the typed accessors do not cover — a list of thresholds, say, or a small object.
func (*Bundle) Model ¶ added in v0.2.0
Model returns the named model, or an error wrapping ErrUnknownModel listing what the bundle does hold. Type-assert the result for a concrete model's own methods.
type Classifier ¶
type Classifier interface {
Model
// Classes returns the class labels, in the column order used by
// PredictProba. The returned slice is a copy and may be modified freely.
Classes() []float64
// PredictProba returns, for each input sample, a vector of class
// probabilities aligned with Classes.
PredictProba(X [][]float64) ([][]float64, error)
// Predict returns, for each input sample, the label of the most probable
// class (ties resolved toward the lowest column index, as in scikit-learn).
Predict(X [][]float64) ([]float64, error)
}
Classifier predicts class membership, mirroring scikit-learn's classifier API. In every method X is a batch of samples (one inner slice per sample, each of length Model.NFeatures); a missing feature is encoded as math.NaN.
func LoadClassifier ¶
func LoadClassifier(r io.Reader) (Classifier, error)
LoadClassifier is Load followed by an assertion that the model classifies.
func LoadClassifierBytes ¶
func LoadClassifierBytes(data []byte) (Classifier, error)
LoadClassifierBytes is LoadBytes followed by a Classifier assertion.
func LoadClassifierFile ¶
func LoadClassifierFile(path string) (Classifier, error)
LoadClassifierFile is LoadFile followed by a Classifier assertion.
Example ¶
ExampleLoadClassifierFile reads an export from disk — the startup-time alternative to embedding it. The model here is the repository's own Iris forest; the two samples are a typical setosa and a typical virginica.
package main
import (
"fmt"
"log"
goml "github.com/heru-opensource/go-ml"
_ "github.com/heru-opensource/go-ml/ensemble"
)
func main() {
clf, err := goml.LoadClassifierFile("testdata/models/iris.json")
if err != nil {
log.Fatal(err)
}
proba, _ := clf.PredictProba([][]float64{
{5.1, 3.5, 1.4, 0.2},
{6.7, 3.0, 5.2, 2.3},
})
labels, _ := clf.Predict([][]float64{
{5.1, 3.5, 1.4, 0.2},
{6.7, 3.0, 5.2, 2.3},
})
for i := range labels {
fmt.Printf("class %g proba %.3f\n", labels[i], proba[i])
}
}
Output: class 0 proba [1.000 0.000 0.000] class 2 proba [0.000 0.003 0.998]
type Decoder ¶
type Decoder func(model json.RawMessage) (Model, error)
A Decoder builds a Model from the type-specific "model" object of an export envelope. Model implementations register one with Register.
type Model ¶
type Model interface {
// Type returns the scikit-learn estimator class name, e.g.
// "RandomForestClassifier". It matches the "type" field of the export.
Type() string
// NFeatures returns the number of input features each sample must have.
NFeatures() int
// FeatureNames returns the input feature names in the column order the
// model expects, or nil when the export carries none — scikit-learn only
// records them (as feature_names_in_) for an estimator fitted on a named
// frame, so nil is ordinary and not an error. When they are present, build
// inputs with an [Assembler] rather than by hand: a feature vector assembled
// in the wrong order is the one mistake this package cannot catch for you,
// because every value is individually valid. The returned slice is a copy.
FeatureNames() []string
}
Model is the behavior common to every model: it knows its scikit-learn estimator type name and how many input features it expects. Concrete models additionally implement a task interface such as Classifier or Regressor.
func Load ¶
Load reads a go-ml/v1 export from r and constructs the model, dispatching on the envelope's "type" to the decoder registered for it.
type Regressor ¶
type Regressor interface {
Model
// Predict returns one predicted target value per input sample.
Predict(X [][]float64) ([]float64, error)
}
Regressor predicts continuous targets. It is defined so the generic Load entry point and registry can serve regression models as they are added; no regressor ships in this package yet.
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
go-ml-bench
command
Command go-ml-bench measures prediction performance of a go-ml model, as a compiled binary, so the numbers are directly comparable to a scikit-learn timing of the same model (see tools/sklexport/bench_compare.py).
|
Command go-ml-bench measures prediction performance of a go-ml model, as a compiled binary, so the numbers are directly comparable to a scikit-learn timing of the same model (see tools/sklexport/bench_compare.py). |
|
go-ml-gen
command
Command go-ml-gen compiles a go-ml/v1 model export into Go source, so the model is linked statically into your binary with no runtime file or parsing.
|
Command go-ml-gen compiles a go-ml/v1 model export into Go source, so the model is linked statically into your binary with no runtime file or parsing. |
|
Package ensemble implements tree-ensemble models: static, parallel reimplementations of the prediction path of scikit-learn's forests.
|
Package ensemble implements tree-ensemble models: static, parallel reimplementations of the prediction path of scikit-learn's forests. |
|
examples
|
|
|
batch
command
Command batch is a runnable example of offline scoring: read a CSV, hand the whole thing to the model in one call, write a CSV back.
|
Command batch is a runnable example of offline scoring: read a CSV, hand the whole thing to the model in one call, write a CSV back. |
|
bundle
command
Command bundle is a runnable example of a model that is not one estimator.
|
Command bundle is a runnable example of a model that is not one estimator. |
|
classify
command
Command classify is a runnable example of using go-ml models that have been compiled statically into the binary with go-ml-gen — there is no Python, no pickle, and no model file at runtime.
|
Command classify is a runnable example of using go-ml models that have been compiled statically into the binary with go-ml-gen — there is no Python, no pickle, and no model file at runtime. |
|
serve
command
Command serve is a runnable example of go-ml in the shape a service uses it: the model is embedded in the binary with //go:embed, decoded once at startup, and then served concurrently — no Python, no model file, nothing to fetch at runtime.
|
Command serve is a runnable example of go-ml in the shape a service uses it: the model is embedded in the binary with //go:embed, decoded once at startup, and then served concurrently — no Python, no model file, nothing to fetch at runtime. |
|
internal
|
|
|
jsonx
Package jsonx holds small JSON helpers shared across go-ml packages.
|
Package jsonx holds small JSON helpers shared across go-ml packages. |
|
Package tree implements binary decision trees whose evaluation is bit-for-bit compatible with scikit-learn's sklearn.tree._tree.Tree.
|
Package tree implements binary decision trees whose evaluation is bit-for-bit compatible with scikit-learn's sklearn.tree._tree.Tree. |