Skip to content

3.0 - #474

Open
andrewdalpino wants to merge 584 commits into
masterfrom
3.0
Open

3.0#474
andrewdalpino wants to merge 584 commits into
masterfrom
3.0

Conversation

@andrewdalpino

Copy link
Copy Markdown
Member
- Integers are now considered a categorical data type
- K Nearest Neighbors and KNN Regressor inference is now parallelized
- Isolation Forest training and inference is now parallelized
- Added disk-based streaming neural network snapshotting
- Can now clear neural network Adaptive optimizer state
- Added validation interval parameter to MLPs and GBM Learners
- Cross Entropy loss function now split into Binary and Multiclass
- Logistic Regression, Softmax, and Adaline now use hold out set
- Adaboost now uses validation set with early stopping window
- Renamed TF-IDF dampening parameter to sublinear
- Exportable Extractors now append by default with option to overwrite
- RBX Serializer tracks major library version number, not minor
- Added Class/Cluster Purity clustering metrics
- V-measure, Completeness, and Homogeneity now use entropy-based formula
- Fixed KDTree edge pruning + optimize traversal
- Ball and Vantage Trees now require Subadditive kernels
- K-d Trees now require Monotonic distance kernels
- Optimize Dataset sort(), sorting is now unstable
- Added per-class smoothing to Gaussian Naive Bayes
- Added per-cluster smoothing to Gaussian Mixture
- You can now exclude certain categories from one-hot encoding
- Fixed SVC save/load using class map sidecar
- Parallel Backends now default to max physical cores not logical
- Added workers() method to the Backend interface (Serial returns 1)
- No longer save/load Backend state, transient per environment
- Added Emoji preset to Regex Filter
- Added Float Type Converter numeric string and ints to float
- Boolean Converter now converts truthy and falsy
- Interval Discretizer now encodes values as integers
- Polynomial Expander now limited to 10'th degree
- Updated to PSR-3 Log version 3
- Update Amp Backend to Amp version 2.0
- Removed Word Stemmer tokenizer
- Removed window early stopping from TSNE
- Removed output layer L2 Penalty parameter from MLP Learners
- RBX serializer now emits warning on class revision mismatch
- Class revision hash now compensates for circular references
- Filesystem Persister now does atomic writes
- Added cleanup() method to remove neural network residual state

Samuel Akopyan added 30 commits March 23, 2026 23:31
andrewdalpino and others added 11 commits September 5, 2026 15:35
* Intial commit

* Expand on new features

* Update Pipeline initialization syntax in documentation

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Update cost function defaults in upgrade documentation

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@@ -86,11 +116,17 @@ public function fit(Dataset $dataset) : void

foreach ($dataset->featureTypes() as $column => $type) {
if ($type->isCategorical()) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found with LLM:

In 3.0, only floats are continuous. Integers are categorical. Several transformers still emit PHP integers (0 / 1 / counts), so they no longer produce continuous data.

Please:

  1. One Hot Encoder, Word Count Vectorizer, and Token Hashing Vectorizer — write floats (0.0, 1.0, float counts), not ints.
  2. TF-IDF and BM25 — also write 0.0 for zero cells. Today only positive values become floats, so a column can mix int 0 and floats.
  3. Docs (upgrading-to-3.md) — say clearly that:
    • integer labels (e.g. [1, 2, 3]) are categorical too, so regressors will reject them unless you cast labels to floats;
    • after One Hot / count vectorizers, add Float Type Converter (or emit floats in the transformer itself) before any continuous estimator or scaler.

Without this, the usual path “One Hot → scaler → KMeans / Ridge / MLP” breaks, even if the user already cast their raw features.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice find, taking a look!

@andrewdalpino andrewdalpino Sep 6, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a tricky one because the output of One Hot Encoder and both word count vectorizers are valid ordered categories. The user has the option to interpret them as an continuous data if they want by casting them to floats using the new Float Type Converter. But I don't want to make that choice for the user - I would rather give them both options and let them decide albeit at the cost of an extra step. I also checked the Scikit implementation and they let you choose the datatype right from the transformer - just a different way of doing it.

I think the fix here is to document these specific examples in the migration guide.

@apphp apphp Sep 6, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, agree - it's better to leave it for user's interpretation.

@apphp
apphp self-requested a review September 6, 2026 18:46
@apphp

apphp commented Sep 6, 2026

Copy link
Copy Markdown

Another found with LLM.

WDYT about this? Does it make sense or relevant at all?

Fix: do the same as AdaBoost — score the model after the new tree is in the ensemble, remember tree count, and roll back only if quality got worse.

Three changes in train():

  1. Move “add tree + update out / outTest” above validation
    Right now you score, then maybe break, then append. Swap that: train booster → $this->ensemble[] = $booster → update predictions → then evalThisStep.

  2. Save size, not epoch

$bestEnsembleSize = 0;
// when score improves (do this BEFORE breaking on max score):
if ($score > $bestScore) {
    $bestScore = $score;
    $bestEpoch = $epoch;
    $bestEnsembleSize = count($this->ensemble);
    $numWorseEpochs = 0;
} else {
    ++$numWorseEpochs;
}
if ($score >= $maxScore) {
    break;
}
  1. Restore by that size, only if the last score is worse
if ($this->scores and end($this->scores) < $bestScore) {
    $this->ensemble = array_slice($this->ensemble, 0, $bestEnsembleSize);
}

Use <, not <=. Use $bestEnsembleSize, not $bestEpoch.

@andrewdalpino

andrewdalpino commented Sep 6, 2026

Copy link
Copy Markdown
Member Author

Which algo are you talking about @apphp?

@apphp

apphp commented Sep 7, 2026

Copy link
Copy Markdown

Which algo are you talking about @apphp?

src/Regressors/GradientBoost.php

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants