sklearn - CyrilB1531/lodestar GitHub Wiki
Verdict: use ML.NET (or SharpLearning for a sklearn-like API), except text
vectorization, which is the gap filled natively by Lodestar.Text (exact
CountVectorizer/TfidfVectorizer semantics), and the two sklearn.decomposition
estimators that work on a sparse matrix.
| sklearn need | Recommended .NET |
|---|---|
| Pipelines, training, deployment |
ML.NET (Microsoft.ML) |
| sklearn-like API (trees, ensembles) | SharpLearning |
CountVectorizer / TfidfVectorizer to the character
|
Lodestar.Text |
classification_report, roc_auc_score, the averaging modes |
Lodestar.Metrics |
TruncatedSVD, NMF(solver="mu") on a sparse matrix |
Lodestar.Decomposition |
PCA on a dense matrix |
ML.NET ProjectToPrincipalComponents on any target, or NumFlat PrincipalComponentAnalysis on net8.0+, or Meta.Numerics PrincipalComponentAnalysis (MS-PL, netstandard2.0). Not Lodestar.Decomposition: centring densifies a CsrMatrix, so PCA is refused for sparse input by name (decision 0004) |
PCA().explained_variance_ratio_ |
Lodestar.Decomposition PrincipalComponentVariance.Compute on any target, at scikit-learn parity โ ML.NET's fourteen public PCA members carry no eigenvalue, and NumFlat's EigenValues ships net8.0 only (decision 0003). Meta.Numerics' PrincipalComponentAnalysis reports it on netstandard2.0 too, 37ร to 809ร slower and refusing a matrix wider than tall (performance) |
StandardScaler on arrays rather than on an IDataView
|
Lodestar.Preprocessing, with the whole scaler surface: StandardScaler, MinMaxScaler, MaxAbsScaler and RobustScaler, fitted whole, over batches or over a CsrMatrix where the reference accepts one |
KFold, StratifiedKFold, train_test_split
|
native โ Splitters in Lodestar.Preprocessing, at scikit-learn's unshuffled folds exactly, with the permutation as an argument rather than a seed. ML.NET TrainTestSplit/CrossValidationSplit group by key and never stratify (dotnet/machinelearning#4396, open since 2019); SharpLearning.CrossValidation StratifiedIndexSampler stratifies over arrays but always shuffles from a seed; decisions/0004
|
MinMaxScaler, RobustScaler, MaxAbsScaler on arrays |
native โ MinMaxScaler, MaxAbsScaler and RobustScaler in Lodestar.Preprocessing, spans in and arrays out, at scikit-learn parity including the range < 10ยทeps floor and numpy.percentile's linear interpolation. Inside a pipeline, ML.NET NormalizeMinMax, NormalizeRobustScaling; decisions/0004
|
OneHotEncoder, OrdinalEncoder, SimpleImputer on arrays |
native โ Encoders and SimpleImputer in Lodestar.Preprocessing, spans in and arrays out, at scikit-learn parity including the code-point category order and the most_frequent tie rule. Inside a pipeline, ML.NET OneHotEncoding, MapValueToKey, ReplaceMissingValues; decisions/0004
|
imblearn SMOTE and resampling |
decisions/0004
|
KMeans(algorithm="lloyd") on arrays |
Lodestar.Cluster KMeans.Fit on any target โ ahead of NumFlat and Meta.Numerics on the same data (performance) |
DBSCAN |
Lodestar.Cluster Dbscan.Fit on any target โ n-dimensional where Dbscan is planar, and ahead of NumFlat on the same data (performance); decisions/0004
|
AgglomerativeClustering |
Lodestar.Cluster AgglomerativeClustering.Fit on any target, ties included. Aglomera (MIT, netstandard1.3, last released 2020) agrees only where no two merge heights tie, and reports Ward on another scale (performance); decisions/0004
|
HDBSCAN |
HdbscanSharp (MIT, netstandard2.0) โ not compared with scikit-learn here; decisions/0004
|
GaussianMixture, k-medoids |
NumFlat GaussianMixtureModel, KMedoids<T> on net8.0+. Below it, scikit-learn-extra, not scikit-learn; decisions/0004
|
MiniBatchKMeans, SpectralClustering
|
decisions/0004
|
dotnet add package Microsoft.MLusing Microsoft.ML;
var ml = new MLContext(seed: 0);
IDataView data = ml.Data.LoadFromTextFile<Row>("data.csv", hasHeader: true, separatorChar: ',');
var pipeline = ml.Transforms.Concatenate("Features", "f1", "f2")
.Append(ml.Regression.Trainers.Sdca(labelColumnName: "Label"));
var model = pipeline.Fit(data);-
TfidfVectorizeris non-standard. The sklearn formula (smooth_idf, per-row L2 normalization) must be reproduced to the character โ ML.NET'sFeaturizeTextdoes not reproduce it. That is exactly the reason forLodestar.Text. See../equivalence.md. -
min_df/max_df, n-gram bounds: on the Lodestar side, not ML.NET. -
Preprocessing is mostly a coupling gap, not an absence. ML.NET has
NormalizeMeanVariance,NormalizeMinMax,OneHotEncoding,ReplaceMissingValues,TrainTestSplitandCrossValidationSplitโ read onMicrosoft.ML5.0.0's exported surface, every one of them is reached through anIDataViewor through a catalog naming columns.NormalizeMeanVariance(TransformsCatalog, string inputColumn, string outputColumn, โฆ)never sees a value.Lodestar.Preprocessinganswers the entry point, not the capability: a caller holding adouble[]gets adouble[]back. Two exceptions are real: ML.NET cannot stratify a split โsamplingKeyColumnNamegroups โ and nothing in .NET does SMOTE.decisions/0004has the reading and the order the rest is written in; until then, ML.NET remains the answer if you are already inside a pipeline.
This is the pitfall that used to read "check the definitions before comparing to sklearn", which names the trap without getting anyone out of it.
precision_score(y_true, y_pred, average=โฆ) returns a different number, not
a different presentation, for each mode. On an imbalanced problem the modes do
not disagree slightly โ they disagree by a factor of two, and every one of them
is arithmetically correct.
A worked example, taken from this repository's own oracle corpus
(binary_imbalanced: 190 samples of class 0, 10 of class 1, a classifier with
30 % label noise). Its confusion matrix is [[133, 57], [4, 6]], so the model
finds 6 of the 10 positives and calls 57 negatives positive:
| Class | Precision | Recall | F1 | Support |
|---|---|---|---|---|
| 0 | 0.971 | 0.700 | 0.813 | 190 |
| 1 | 0.095 | 0.600 | 0.164 | 10 |
average= |
Precision | Recall | F1 | What it means |
|---|---|---|---|---|
"micro" |
0.695 | 0.695 | 0.695 | Pool every sample, then score once. On a full label set this is accuracy. |
"macro" |
0.533 | 0.650 | 0.489 | Mean of the per-class scores. The 10-sample class weighs exactly as much as the 190-sample one. |
"weighted" |
0.927 | 0.695 | 0.781 | Mean of the per-class scores weighted by support. The majority class dominates. |
"binary" |
0.095 | 0.600 | 0.164 | Not an average: class posLabel alone, ignoring the other. sklearn's default. |
Macro F1 says 0.489, weighted F1 says 0.781, for one model on one dataset. Report either without naming the mode and the reader learns nothing. The two are answering different questions: macro asks how the model does on a class picked at random, weighted asks how it does on a sample picked at random.
In C#, the mode is an enum rather than a string, so a typo is a compile error
instead of a ValueError at the end of a run. One
ConfusionMatrix.Compute
pass feeds both
F1.Score and
ClassificationReport.Compute:
using Lodestar.Metrics;
ConfusionMatrix cm = ConfusionMatrix.Compute(yTrue, yPred); // one O(samples) pass
double macro = F1.Score(cm, Averaging.Macro); // 0.489
double weighted = F1.Score(cm, Averaging.Weighted); // 0.781
double[] perClass = F1.PerClass(cm); // [0.813, 0.164]
Console.WriteLine(ClassificationReport.Compute(cm).ToText()); // what sklearn printsTwo differences from the Python spelling are deliberate. average=None becomes
F1.PerClass, a method,
because it returns one value per class rather than a scalar โ an enum member
cannot change its method's return type. And
Averaging.Binary throws on a target with more than two classes instead of
guessing which class was meant. Both are recorded in
../decisions/0003.
Absent classes. A class with no predictions gives 0/0. sklearn returns 0 and
emits an UndefinedMetricWarning; a warning is easy to miss in a log and has no
natural .NET equivalent. Lodestar.Metrics makes the choice explicit โ
ZeroDivision.Zero (sklearn's value), One, NaN, or Throw, which raises
UndefinedMetricException rather than letting a silent 0 flow into a report.
Every function, with its sklearn call and its deliberate divergences, is in
../equivalence.md.
dotnet add package Lodestar.MetricsGuide to be expanded as real needs arise.