Tree-based featurization - #3812

Merged
wschin merged 27 commits into
dotnet:masterfrom
wschin:tree-feat
Jun 26, 2019
Merged

Tree-based featurization#3812
wschin merged 27 commits into
dotnet:masterfrom
wschin:tree-feat

Conversation

@wschin

@wschinwschin commented Jun 3, 2019

Copy link
Copy Markdown
Contributor

Fix#2482. Generating features using tree structure has been a popular technique in data mining. This PR exposes this internal-only feature to the public.

Since I don't have enough time to handle multiple different assignments at the same time, please don't put nit comments and create new issues instead. Thanks a lot.

@wschinwschin self-assigned this Jun 3, 2019
return new FastForestBinaryTrainer(env, options);
}

public static PretrainedTreeFeaturizationEstimator PretrainTreeEnsembleFeaturizing(this TransformsCatalog catalog,

@eerhardteerhardtJun 3, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

XML Doc on all public classes and APIs. #Resolved

@wschinwschinJun 3, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

No problem. I am working on them. #Resolved

return new PretrainedTreeFeaturizationEstimator(env, options);
}

public static FastForestRegressionFeaturizationEstimator FastForestRegressionFeaturizing(this TransformsCatalog catalog,

@eerhardteerhardtJun 3, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This naming pattern reads a little funny. How about turning it into FeaturizeXXX? Like we have with FeaturizeText. #Resolved

@wschinwschinJun 3, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I can do FeaturizeBy.... #Resolved

@eerhardteerhardtJun 3, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That sounds better (to me at least). #Resolved

/// "Leaves" + <see cref="OutputColumnsSuffix"/>, and "Paths" + <see cref="OutputColumnsSuffix"/>. If <see cref="OutputColumnsSuffix"/>
/// is <see langword="null"/>, the output names would be "Trees", "Leaves", and "Paths".
/// </summary>
public string OutputColumnsSuffix;

@justinormontjustinormontJun 3, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We went away from magic strings in the TextTransform. Previously with tokens=+, we produced a new column named {OutputColName}_TokenizedText.

For the estimators API we have users directly enter the column name for the tokens. We may want to do the same for the Trees/Leaves/Paths of the TreeFeat.

Perhaps:
OutputColumnTreeName, OutputColumnLeavesName, OutputColumnPathsName.

#Resolved

@Ivanidzo4kaIvanidzo4kaJun 4, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

And don't add that column if they empty or equal to null.
That way you can actually configure which parts of tree structure do you want. #Resolved

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

As further background on the PR I was referencing...

Conversation about TextTransform:
via @rogancarr in #2957 PR

When using OutputTokens=true, FeaturizeText creates a new column called ${OutputColumnName}_TransformedText. This isn't really well documented anywhere, and it's odd behavior. I suggest that we make the tokenized text column name explicit in the API.

My suggestion would be the following:

  • Change OutputTokens = [bool] to OutputTokensColumn = [string], and a string.NullOrWhitespace(OutputTokensColumn) signifies that this column will not be created. #Resolved

@wschinwschinJun 5, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Now we can do optional output columns and custom output column names. Please see tests for examples (or wait for formal API samples). #Resolved

/// <param name="catalog">The context <see cref="TransformsCatalog"/> to create <see cref="FastTreeTweedieFeaturizationEstimator"/>.</param>
/// <param name="options">The options to configure <see cref="FastTreeTweedieFeaturizationEstimator"/>. See <see cref="FastTreeTweedieFeaturizationEstimator.Options"/> and
/// <see cref="TreeEnsembleFeaturizationEstimatorBase.CommonOptions"/> for available settings.</param>
public static FastTreeTweedieFeaturizationEstimator FeaturizeByFastTreeTweedie(this TransformsCatalog catalog,

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

May want to note in its name that FastTreeTweedie is regression: (the naming of the others list their task types)

Suggested change
publicstaticFastTreeTweedieFeaturizationEstimatorFeaturizeByFastTreeTweedie(thisTransformsCatalogcatalog,
publicstaticFastTreeTweedieRegressionFeaturizationEstimatorFeaturizeByFastTreeTweedieRegression(thisTransformsCatalogcatalog,
``` #ByDesign

@wschinwschinJun 5, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Yes if the model name doesn't tell the task. Given that Tweedie somehow implies a regression case, we don't have Regression appended to any of public Tweedie modules. This pattern can be seen in FastTreeTweedieTrainer and FastTreeTweedieModelParameters. #Resolved

TrainerOptions = trainerOptions
};

var pipeline = ML.Transforms.FeaturizeByFastTreeBinary(options).

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Creating (8) seperate ML.Transforms.FeaturizeBy{ FastTreeBinary, FastForestRegression, FastTreeRegression, FastTreeTweedie, ... } featurizers under ML.Transforms.* seems a bit large. Seems to clutter up the namespace.

In the future, this list should grow as I think we should have LightGBM variants too (and CatBoost if we take it in). The number of independent featurizers will be:
{ FastTree, FastTreeTweedie, FastForest, LightGBM, CatBoost } x { BinaryClassification, Multiclass, Regression, Ranking }`. (with some combinations missing)

Would it be more clean to have one ML.Transforms.TreeFeaturizer(), and put the specific instance type as a parameter? Would it be doable to have one return type? #ByDesign

@wschinwschinJun 5, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

There are two reasons that I don't like a single TreeFeaturizer.

  1. It goes toward an opposite direction of the C# API's design. Most of them are strongly typed to the underlying data structures. You can see we have a lot of FastTree... and LightGbm..., which is intended.
  2. It may requires user to specify TreeFeaturizer<TTrainer>(options) and the user manually needs to make sure the type of options is TTrainer.Options, which is easy to make mistakes. #Resolved

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You're right, the current pattern will likely have less mistakes by the user.

For the plethora of FastTree... and LightGBM..., those are namespaced under the task mlContext.Regression.Trainers.FastTree(), hence the duplication isn't visible.

TrainerOptions = trainerOptions
};

var pipeline = ML.Transforms.FeaturizeByFastForestRegression(options).

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you add a test of ML.Transforms.FeaturizeByFastForestRegression() using FastForest's ShuffleLabels option? I believe this is the only way to use TreeFeat w/ multi-class classification.

@wschinwschinJun 6, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Let's make it in another PR. This PR has been too large.. #Resolved

@justinormontjustinormontJun 6, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems the UnitTests should be in the main PR. Specifically, I think we should ensure the multi-class case can work. #Resolved

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for adding.
#Resolved

@wschinwschin changed the title [WIP] Tree-based featurizationTree-based featurizationJun 6, 2019
@wschin
wschin requested review from Ivanidzo4ka, eerhardt and justinormont and removed request for Ivanidzo4ka and justinormontJune 6, 2019 17:58
Comment threadsrc/Microsoft.ML.FastTree/TreeEnsembleFeaturizer.cs
@wschin
wschin requested a review from eerhardtJune 6, 2019 22:10
Comment threadtest/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs Outdated
Comment threadtest/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs Outdated
}

[Fact]
public void TreeEnsembleFeaturizingPipelineMulticlass()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@daholste and I were able to map the Key to a float using a CustomMapping().

I'd recommend the multiclass unit test be:

Suggested change
publicvoidTreeEnsembleFeaturizingPipelineMulticlass()
[Fact]
publicvoidTreeEnsembleFeaturizingPipelineMulticlass()
{
intdataPointCount=1000;
vardata=SamplesUtils.DatasetUtils.GenerateRandomMulticlassClassificationExamples(dataPointCount).ToList();
vardataView=ML.Data.LoadFromEnumerable(data);
dataView=ML.Data.Cache(dataView);
vartrainerOptions=newFastForestRegressionTrainer.Options
{
NumberOfThreads=1,
NumberOfTrees=10,
NumberOfLeaves=4,
MinimumExampleCountPerLeaf=10,
FeatureColumnName="Features",
LabelColumnName="FloatLabel",
ShuffleLabels=true
};
varoptions=newFastForestRegressionFeaturizationEstimator.Options()
{
InputColumnName="Features",
TreesColumnName="Trees",
LeavesColumnName="Leaves",
PathsColumnName="Paths",
TrainerOptions= trainerOptions
};
Action<RowWithKey,RowWithFloat>actionConvertKeyToFloat=(RowWithKeyrowWithKey,RowWithFloatrowWithFloat)=>
{
rowWithFloat.FloatLabel=rowWithKey.KeyLabel==0?float.NaN:rowWithKey.KeyLabel-1;
};
varsplit=ML.Data.TrainTestSplit(dataView,0.5);
vartrainData=split.TrainSet;
vartestData=split.TestSet;
varpipeline=ML.Transforms.Conversion.MapValueToKey("KeyLabel","Label")
.Append(ML.Transforms.CustomMapping(actionConvertKeyToFloat,"KeyLabel"))
.Append(ML.Transforms.FeaturizeByFastForestRegression(options))
.Append(ML.Transforms.Concatenate("CombinedFeatures","Trees","Leaves","Paths"))
.Append(ML.MulticlassClassification.Trainers.SdcaMaximumEntropy("KeyLabel","CombinedFeatures"));
varmodel=pipeline.Fit(trainData);
varprediction=model.Transform(testData);
varmetrics=ML.MulticlassClassification.Evaluate(prediction,labelColumnName:"KeyLabel");
Assert.True(metrics.MacroAccuracy>0.6);
Assert.True(metrics.MicroAccuracy>0.6);
}
class RowWithKey
{
[KeyType()]
publicuintKeyLabel{get;set;}
}
class RowWithFloat
{
publicfloatFloatLabel{get;set;}
}

Specifically, this is using a CustomMapping() to convert the Key to a float for use in the TreeFeat's FastForest regression. The current method in the unit test requires a user to know/list all of the values in their Label (and their type). The CustomMapping() style is easier for a user to replicate for their dataset.

We also added a TrainTestSplit() so we're not testing on the training set, and we removed the original features from the Concatenate() to ensure the TreeFeat's output features are useful.

@codemzs
codemzs requested review from artidoro and ganikJune 17, 2019 17:11
public TreeEnsembleModelParameters ModelParameters;
};

private TreeEnsembleModelParameters _modelParameters;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

TreeEnsembleModelParameters [](start = 16, length = 27)

Should this be readonly or something similar to make sure it is not altered?

@artidoro

artidoro commented Jun 20, 2019

Copy link
Copy Markdown
Contributor

Since you have already built all this infrastructure why are we not providing the featurizers for LightGbm trainers? I think they still use the same base class for the tree ensemble.
I guess this could come in another PR.

// The 0-1 encoding of leaves the input feature vector falls into.
public float[] Leaves { get; set; }
// The 0-1 encoding of paths the input feature vector reaches the leaves.
public float[] Paths { get; set; }

@artidoroartidoroJun 20, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit (same in other places if you are doing a revision of the PR):
// The 0-1 encoding of paths the input feature vector follows to reach the leaves.

/// and the i-th vector element is the prediction value predicted by the i-th tree.
/// If <see cref="TreesColumnName"/> is <see langword="null"/>, this output column may not be generated.
/// </summary>
public string TreesColumnName;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

TreesColumnName [](start = 26, length = 15)

Suggested renaming:

TreesColumnName -> TreeOutputsColumnName

I think it would be easier to understand, but this is not necessary.

@artidoroartidoro left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

:shipit:

@abgoswamabgoswam left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

:shipit:

@wschin
wschin merged commit 9d29111 into dotnet:masterJun 26, 2019
@wschin
wschin deleted the tree-feat branch June 26, 2019 23:15
Dmitry-A pushed a commit to Dmitry-A/machinelearning that referenced this pull request Jul 24, 2019
* Implement transformer
* Initial draft of porting tree-based featurization
* Internalize something
* Add Tweedie and Ranking cases
* Some small docs
* Customize output column names
* Fix save and load
* Optional output columns
* Fix a test and add some XML docs
* Add samples
* Add a sample
* API docs
* Fix one line
* Add MC test
* Extend a test further
* Address some comments
* Address some comments
* Address comments
* Comment
* Add cache points
* Update test/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs
Co-Authored-By: Justin Ormont <justinormont@users.noreply.github.com>
* Address comment
* Add Justin's test
* Reduce sample size
* Update sample output
@ghostghost locked as resolved and limited conversation to collaborators Mar 21, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TreeEnsembleFeaturizer is not a Transformer yet

6 participants

@wschin@artidoro@Ivanidzo4ka@justinormont@eerhardt@abgoswam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Tree-based featurization - #3812

Merged
wschin merged 27 commits into
dotnet:masterfrom
wschin:tree-feat
Jun 26, 2019
Merged

Tree-based featurization#3812
wschin merged 27 commits into
dotnet:masterfrom
wschin:tree-feat

Conversation

@wschin

@wschinwschin commented Jun 3, 2019

Copy link
Copy Markdown
Contributor

Fix#2482. Generating features using tree structure has been a popular technique in data mining. This PR exposes this internal-only feature to the public.

Since I don't have enough time to handle multiple different assignments at the same time, please don't put nit comments and create new issues instead. Thanks a lot.

@wschinwschin self-assigned this Jun 3, 2019
return new FastForestBinaryTrainer(env, options);
}

public static PretrainedTreeFeaturizationEstimator PretrainTreeEnsembleFeaturizing(this TransformsCatalog catalog,

@eerhardteerhardtJun 3, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

XML Doc on all public classes and APIs. #Resolved

@wschinwschinJun 3, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

No problem. I am working on them. #Resolved

return new PretrainedTreeFeaturizationEstimator(env, options);
}

public static FastForestRegressionFeaturizationEstimator FastForestRegressionFeaturizing(this TransformsCatalog catalog,

@eerhardteerhardtJun 3, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This naming pattern reads a little funny. How about turning it into FeaturizeXXX? Like we have with FeaturizeText. #Resolved

@wschinwschinJun 3, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I can do FeaturizeBy.... #Resolved

@eerhardteerhardtJun 3, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That sounds better (to me at least). #Resolved

/// "Leaves" + <see cref="OutputColumnsSuffix"/>, and "Paths" + <see cref="OutputColumnsSuffix"/>. If <see cref="OutputColumnsSuffix"/>
/// is <see langword="null"/>, the output names would be "Trees", "Leaves", and "Paths".
/// </summary>
public string OutputColumnsSuffix;

@justinormontjustinormontJun 3, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We went away from magic strings in the TextTransform. Previously with tokens=+, we produced a new column named {OutputColName}_TokenizedText.

For the estimators API we have users directly enter the column name for the tokens. We may want to do the same for the Trees/Leaves/Paths of the TreeFeat.

Perhaps:
OutputColumnTreeName, OutputColumnLeavesName, OutputColumnPathsName.

#Resolved

@Ivanidzo4kaIvanidzo4kaJun 4, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

And don't add that column if they empty or equal to null.
That way you can actually configure which parts of tree structure do you want. #Resolved

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

As further background on the PR I was referencing...

Conversation about TextTransform:
via @rogancarr in #2957 PR

When using OutputTokens=true, FeaturizeText creates a new column called ${OutputColumnName}_TransformedText. This isn't really well documented anywhere, and it's odd behavior. I suggest that we make the tokenized text column name explicit in the API.

My suggestion would be the following:

  • Change OutputTokens = [bool] to OutputTokensColumn = [string], and a string.NullOrWhitespace(OutputTokensColumn) signifies that this column will not be created. #Resolved

@wschinwschinJun 5, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Now we can do optional output columns and custom output column names. Please see tests for examples (or wait for formal API samples). #Resolved

/// <param name="catalog">The context <see cref="TransformsCatalog"/> to create <see cref="FastTreeTweedieFeaturizationEstimator"/>.</param>
/// <param name="options">The options to configure <see cref="FastTreeTweedieFeaturizationEstimator"/>. See <see cref="FastTreeTweedieFeaturizationEstimator.Options"/> and
/// <see cref="TreeEnsembleFeaturizationEstimatorBase.CommonOptions"/> for available settings.</param>
public static FastTreeTweedieFeaturizationEstimator FeaturizeByFastTreeTweedie(this TransformsCatalog catalog,

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

May want to note in its name that FastTreeTweedie is regression: (the naming of the others list their task types)

Suggested change
publicstaticFastTreeTweedieFeaturizationEstimatorFeaturizeByFastTreeTweedie(thisTransformsCatalogcatalog,
publicstaticFastTreeTweedieRegressionFeaturizationEstimatorFeaturizeByFastTreeTweedieRegression(thisTransformsCatalogcatalog,
``` #ByDesign

@wschinwschinJun 5, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Yes if the model name doesn't tell the task. Given that Tweedie somehow implies a regression case, we don't have Regression appended to any of public Tweedie modules. This pattern can be seen in FastTreeTweedieTrainer and FastTreeTweedieModelParameters. #Resolved

TrainerOptions = trainerOptions
};

var pipeline = ML.Transforms.FeaturizeByFastTreeBinary(options).

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Creating (8) seperate ML.Transforms.FeaturizeBy{ FastTreeBinary, FastForestRegression, FastTreeRegression, FastTreeTweedie, ... } featurizers under ML.Transforms.* seems a bit large. Seems to clutter up the namespace.

In the future, this list should grow as I think we should have LightGBM variants too (and CatBoost if we take it in). The number of independent featurizers will be:
{ FastTree, FastTreeTweedie, FastForest, LightGBM, CatBoost } x { BinaryClassification, Multiclass, Regression, Ranking }`. (with some combinations missing)

Would it be more clean to have one ML.Transforms.TreeFeaturizer(), and put the specific instance type as a parameter? Would it be doable to have one return type? #ByDesign

@wschinwschinJun 5, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

There are two reasons that I don't like a single TreeFeaturizer.

  1. It goes toward an opposite direction of the C# API's design. Most of them are strongly typed to the underlying data structures. You can see we have a lot of FastTree... and LightGbm..., which is intended.
  2. It may requires user to specify TreeFeaturizer<TTrainer>(options) and the user manually needs to make sure the type of options is TTrainer.Options, which is easy to make mistakes. #Resolved

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You're right, the current pattern will likely have less mistakes by the user.

For the plethora of FastTree... and LightGBM..., those are namespaced under the task mlContext.Regression.Trainers.FastTree(), hence the duplication isn't visible.

TrainerOptions = trainerOptions
};

var pipeline = ML.Transforms.FeaturizeByFastForestRegression(options).

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you add a test of ML.Transforms.FeaturizeByFastForestRegression() using FastForest's ShuffleLabels option? I believe this is the only way to use TreeFeat w/ multi-class classification.

@wschinwschinJun 6, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Let's make it in another PR. This PR has been too large.. #Resolved

@justinormontjustinormontJun 6, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems the UnitTests should be in the main PR. Specifically, I think we should ensure the multi-class case can work. #Resolved

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for adding.
#Resolved

@wschinwschin changed the title [WIP] Tree-based featurizationTree-based featurizationJun 6, 2019
@wschin
wschin requested review from Ivanidzo4ka, eerhardt and justinormont and removed request for Ivanidzo4ka and justinormontJune 6, 2019 17:58
Comment threadsrc/Microsoft.ML.FastTree/TreeEnsembleFeaturizer.cs
@wschin
wschin requested a review from eerhardtJune 6, 2019 22:10
Comment threadtest/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs Outdated
Comment threadtest/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs Outdated
}

[Fact]
public void TreeEnsembleFeaturizingPipelineMulticlass()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@daholste and I were able to map the Key to a float using a CustomMapping().

I'd recommend the multiclass unit test be:

Suggested change
publicvoidTreeEnsembleFeaturizingPipelineMulticlass()
[Fact]
publicvoidTreeEnsembleFeaturizingPipelineMulticlass()
{
intdataPointCount=1000;
vardata=SamplesUtils.DatasetUtils.GenerateRandomMulticlassClassificationExamples(dataPointCount).ToList();
vardataView=ML.Data.LoadFromEnumerable(data);
dataView=ML.Data.Cache(dataView);
vartrainerOptions=newFastForestRegressionTrainer.Options
{
NumberOfThreads=1,
NumberOfTrees=10,
NumberOfLeaves=4,
MinimumExampleCountPerLeaf=10,
FeatureColumnName="Features",
LabelColumnName="FloatLabel",
ShuffleLabels=true
};
varoptions=newFastForestRegressionFeaturizationEstimator.Options()
{
InputColumnName="Features",
TreesColumnName="Trees",
LeavesColumnName="Leaves",
PathsColumnName="Paths",
TrainerOptions= trainerOptions
};
Action<RowWithKey,RowWithFloat>actionConvertKeyToFloat=(RowWithKeyrowWithKey,RowWithFloatrowWithFloat)=>
{
rowWithFloat.FloatLabel=rowWithKey.KeyLabel==0?float.NaN:rowWithKey.KeyLabel-1;
};
varsplit=ML.Data.TrainTestSplit(dataView,0.5);
vartrainData=split.TrainSet;
vartestData=split.TestSet;
varpipeline=ML.Transforms.Conversion.MapValueToKey("KeyLabel","Label")
.Append(ML.Transforms.CustomMapping(actionConvertKeyToFloat,"KeyLabel"))
.Append(ML.Transforms.FeaturizeByFastForestRegression(options))
.Append(ML.Transforms.Concatenate("CombinedFeatures","Trees","Leaves","Paths"))
.Append(ML.MulticlassClassification.Trainers.SdcaMaximumEntropy("KeyLabel","CombinedFeatures"));
varmodel=pipeline.Fit(trainData);
varprediction=model.Transform(testData);
varmetrics=ML.MulticlassClassification.Evaluate(prediction,labelColumnName:"KeyLabel");
Assert.True(metrics.MacroAccuracy>0.6);
Assert.True(metrics.MicroAccuracy>0.6);
}
class RowWithKey
{
[KeyType()]
publicuintKeyLabel{get;set;}
}
class RowWithFloat
{
publicfloatFloatLabel{get;set;}
}

Specifically, this is using a CustomMapping() to convert the Key to a float for use in the TreeFeat's FastForest regression. The current method in the unit test requires a user to know/list all of the values in their Label (and their type). The CustomMapping() style is easier for a user to replicate for their dataset.

We also added a TrainTestSplit() so we're not testing on the training set, and we removed the original features from the Concatenate() to ensure the TreeFeat's output features are useful.

@codemzs
codemzs requested review from artidoro and ganikJune 17, 2019 17:11
public TreeEnsembleModelParameters ModelParameters;
};

private TreeEnsembleModelParameters _modelParameters;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

TreeEnsembleModelParameters [](start = 16, length = 27)

Should this be readonly or something similar to make sure it is not altered?

@artidoro

artidoro commented Jun 20, 2019

Copy link
Copy Markdown
Contributor

Since you have already built all this infrastructure why are we not providing the featurizers for LightGbm trainers? I think they still use the same base class for the tree ensemble.
I guess this could come in another PR.

// The 0-1 encoding of leaves the input feature vector falls into.
public float[] Leaves { get; set; }
// The 0-1 encoding of paths the input feature vector reaches the leaves.
public float[] Paths { get; set; }

@artidoroartidoroJun 20, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit (same in other places if you are doing a revision of the PR):
// The 0-1 encoding of paths the input feature vector follows to reach the leaves.

/// and the i-th vector element is the prediction value predicted by the i-th tree.
/// If <see cref="TreesColumnName"/> is <see langword="null"/>, this output column may not be generated.
/// </summary>
public string TreesColumnName;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

TreesColumnName [](start = 26, length = 15)

Suggested renaming:

TreesColumnName -> TreeOutputsColumnName

I think it would be easier to understand, but this is not necessary.

@artidoroartidoro left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

:shipit:

@abgoswamabgoswam left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

:shipit:

@wschin
wschin merged commit 9d29111 into dotnet:masterJun 26, 2019
@wschin
wschin deleted the tree-feat branch June 26, 2019 23:15
Dmitry-A pushed a commit to Dmitry-A/machinelearning that referenced this pull request Jul 24, 2019
* Implement transformer
* Initial draft of porting tree-based featurization
* Internalize something
* Add Tweedie and Ranking cases
* Some small docs
* Customize output column names
* Fix save and load
* Optional output columns
* Fix a test and add some XML docs
* Add samples
* Add a sample
* API docs
* Fix one line
* Add MC test
* Extend a test further
* Address some comments
* Address some comments
* Address comments
* Comment
* Add cache points
* Update test/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs
Co-Authored-By: Justin Ormont <justinormont@users.noreply.github.com>
* Address comment
* Add Justin's test
* Reduce sample size
* Update sample output
@ghostghost locked as resolved and limited conversation to collaborators Mar 21, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TreeEnsembleFeaturizer is not a Transformer yet

6 participants

@wschin@artidoro@Ivanidzo4ka@justinormont@eerhardt@abgoswam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Tree-based featurization - #3812

Merged
wschin merged 27 commits into
dotnet:masterfrom
wschin:tree-feat
Jun 26, 2019
Merged

Tree-based featurization#3812
wschin merged 27 commits into
dotnet:masterfrom
wschin:tree-feat

Conversation

@wschin

@wschinwschin commented Jun 3, 2019

Copy link
Copy Markdown
Contributor

Fix#2482. Generating features using tree structure has been a popular technique in data mining. This PR exposes this internal-only feature to the public.

Since I don't have enough time to handle multiple different assignments at the same time, please don't put nit comments and create new issues instead. Thanks a lot.

@wschinwschin self-assigned this Jun 3, 2019
return new FastForestBinaryTrainer(env, options);
}

public static PretrainedTreeFeaturizationEstimator PretrainTreeEnsembleFeaturizing(this TransformsCatalog catalog,

@eerhardteerhardtJun 3, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

XML Doc on all public classes and APIs. #Resolved

@wschinwschinJun 3, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

No problem. I am working on them. #Resolved

return new PretrainedTreeFeaturizationEstimator(env, options);
}

public static FastForestRegressionFeaturizationEstimator FastForestRegressionFeaturizing(this TransformsCatalog catalog,

@eerhardteerhardtJun 3, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This naming pattern reads a little funny. How about turning it into FeaturizeXXX? Like we have with FeaturizeText. #Resolved

@wschinwschinJun 3, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I can do FeaturizeBy.... #Resolved

@eerhardteerhardtJun 3, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That sounds better (to me at least). #Resolved

/// "Leaves" + <see cref="OutputColumnsSuffix"/>, and "Paths" + <see cref="OutputColumnsSuffix"/>. If <see cref="OutputColumnsSuffix"/>
/// is <see langword="null"/>, the output names would be "Trees", "Leaves", and "Paths".
/// </summary>
public string OutputColumnsSuffix;

@justinormontjustinormontJun 3, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We went away from magic strings in the TextTransform. Previously with tokens=+, we produced a new column named {OutputColName}_TokenizedText.

For the estimators API we have users directly enter the column name for the tokens. We may want to do the same for the Trees/Leaves/Paths of the TreeFeat.

Perhaps:
OutputColumnTreeName, OutputColumnLeavesName, OutputColumnPathsName.

#Resolved

@Ivanidzo4kaIvanidzo4kaJun 4, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

And don't add that column if they empty or equal to null.
That way you can actually configure which parts of tree structure do you want. #Resolved

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

As further background on the PR I was referencing...

Conversation about TextTransform:
via @rogancarr in #2957 PR

When using OutputTokens=true, FeaturizeText creates a new column called ${OutputColumnName}_TransformedText. This isn't really well documented anywhere, and it's odd behavior. I suggest that we make the tokenized text column name explicit in the API.

My suggestion would be the following:

  • Change OutputTokens = [bool] to OutputTokensColumn = [string], and a string.NullOrWhitespace(OutputTokensColumn) signifies that this column will not be created. #Resolved

@wschinwschinJun 5, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Now we can do optional output columns and custom output column names. Please see tests for examples (or wait for formal API samples). #Resolved

/// <param name="catalog">The context <see cref="TransformsCatalog"/> to create <see cref="FastTreeTweedieFeaturizationEstimator"/>.</param>
/// <param name="options">The options to configure <see cref="FastTreeTweedieFeaturizationEstimator"/>. See <see cref="FastTreeTweedieFeaturizationEstimator.Options"/> and
/// <see cref="TreeEnsembleFeaturizationEstimatorBase.CommonOptions"/> for available settings.</param>
public static FastTreeTweedieFeaturizationEstimator FeaturizeByFastTreeTweedie(this TransformsCatalog catalog,

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

May want to note in its name that FastTreeTweedie is regression: (the naming of the others list their task types)

Suggested change
publicstaticFastTreeTweedieFeaturizationEstimatorFeaturizeByFastTreeTweedie(thisTransformsCatalogcatalog,
publicstaticFastTreeTweedieRegressionFeaturizationEstimatorFeaturizeByFastTreeTweedieRegression(thisTransformsCatalogcatalog,
``` #ByDesign

@wschinwschinJun 5, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Yes if the model name doesn't tell the task. Given that Tweedie somehow implies a regression case, we don't have Regression appended to any of public Tweedie modules. This pattern can be seen in FastTreeTweedieTrainer and FastTreeTweedieModelParameters. #Resolved

TrainerOptions = trainerOptions
};

var pipeline = ML.Transforms.FeaturizeByFastTreeBinary(options).

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Creating (8) seperate ML.Transforms.FeaturizeBy{ FastTreeBinary, FastForestRegression, FastTreeRegression, FastTreeTweedie, ... } featurizers under ML.Transforms.* seems a bit large. Seems to clutter up the namespace.

In the future, this list should grow as I think we should have LightGBM variants too (and CatBoost if we take it in). The number of independent featurizers will be:
{ FastTree, FastTreeTweedie, FastForest, LightGBM, CatBoost } x { BinaryClassification, Multiclass, Regression, Ranking }`. (with some combinations missing)

Would it be more clean to have one ML.Transforms.TreeFeaturizer(), and put the specific instance type as a parameter? Would it be doable to have one return type? #ByDesign

@wschinwschinJun 5, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

There are two reasons that I don't like a single TreeFeaturizer.

  1. It goes toward an opposite direction of the C# API's design. Most of them are strongly typed to the underlying data structures. You can see we have a lot of FastTree... and LightGbm..., which is intended.
  2. It may requires user to specify TreeFeaturizer<TTrainer>(options) and the user manually needs to make sure the type of options is TTrainer.Options, which is easy to make mistakes. #Resolved

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You're right, the current pattern will likely have less mistakes by the user.

For the plethora of FastTree... and LightGBM..., those are namespaced under the task mlContext.Regression.Trainers.FastTree(), hence the duplication isn't visible.

TrainerOptions = trainerOptions
};

var pipeline = ML.Transforms.FeaturizeByFastForestRegression(options).

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you add a test of ML.Transforms.FeaturizeByFastForestRegression() using FastForest's ShuffleLabels option? I believe this is the only way to use TreeFeat w/ multi-class classification.

@wschinwschinJun 6, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Let's make it in another PR. This PR has been too large.. #Resolved

@justinormontjustinormontJun 6, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems the UnitTests should be in the main PR. Specifically, I think we should ensure the multi-class case can work. #Resolved

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for adding.
#Resolved

@wschinwschin changed the title [WIP] Tree-based featurizationTree-based featurizationJun 6, 2019
@wschin
wschin requested review from Ivanidzo4ka, eerhardt and justinormont and removed request for Ivanidzo4ka and justinormontJune 6, 2019 17:58
Comment threadsrc/Microsoft.ML.FastTree/TreeEnsembleFeaturizer.cs
@wschin
wschin requested a review from eerhardtJune 6, 2019 22:10
Comment threadtest/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs Outdated
Comment threadtest/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs Outdated
}

[Fact]
public void TreeEnsembleFeaturizingPipelineMulticlass()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@daholste and I were able to map the Key to a float using a CustomMapping().

I'd recommend the multiclass unit test be:

Suggested change
publicvoidTreeEnsembleFeaturizingPipelineMulticlass()
[Fact]
publicvoidTreeEnsembleFeaturizingPipelineMulticlass()
{
intdataPointCount=1000;
vardata=SamplesUtils.DatasetUtils.GenerateRandomMulticlassClassificationExamples(dataPointCount).ToList();
vardataView=ML.Data.LoadFromEnumerable(data);
dataView=ML.Data.Cache(dataView);
vartrainerOptions=newFastForestRegressionTrainer.Options
{
NumberOfThreads=1,
NumberOfTrees=10,
NumberOfLeaves=4,
MinimumExampleCountPerLeaf=10,
FeatureColumnName="Features",
LabelColumnName="FloatLabel",
ShuffleLabels=true
};
varoptions=newFastForestRegressionFeaturizationEstimator.Options()
{
InputColumnName="Features",
TreesColumnName="Trees",
LeavesColumnName="Leaves",
PathsColumnName="Paths",
TrainerOptions= trainerOptions
};
Action<RowWithKey,RowWithFloat>actionConvertKeyToFloat=(RowWithKeyrowWithKey,RowWithFloatrowWithFloat)=>
{
rowWithFloat.FloatLabel=rowWithKey.KeyLabel==0?float.NaN:rowWithKey.KeyLabel-1;
};
varsplit=ML.Data.TrainTestSplit(dataView,0.5);
vartrainData=split.TrainSet;
vartestData=split.TestSet;
varpipeline=ML.Transforms.Conversion.MapValueToKey("KeyLabel","Label")
.Append(ML.Transforms.CustomMapping(actionConvertKeyToFloat,"KeyLabel"))
.Append(ML.Transforms.FeaturizeByFastForestRegression(options))
.Append(ML.Transforms.Concatenate("CombinedFeatures","Trees","Leaves","Paths"))
.Append(ML.MulticlassClassification.Trainers.SdcaMaximumEntropy("KeyLabel","CombinedFeatures"));
varmodel=pipeline.Fit(trainData);
varprediction=model.Transform(testData);
varmetrics=ML.MulticlassClassification.Evaluate(prediction,labelColumnName:"KeyLabel");
Assert.True(metrics.MacroAccuracy>0.6);
Assert.True(metrics.MicroAccuracy>0.6);
}
class RowWithKey
{
[KeyType()]
publicuintKeyLabel{get;set;}
}
class RowWithFloat
{
publicfloatFloatLabel{get;set;}
}

Specifically, this is using a CustomMapping() to convert the Key to a float for use in the TreeFeat's FastForest regression. The current method in the unit test requires a user to know/list all of the values in their Label (and their type). The CustomMapping() style is easier for a user to replicate for their dataset.

We also added a TrainTestSplit() so we're not testing on the training set, and we removed the original features from the Concatenate() to ensure the TreeFeat's output features are useful.

@codemzs
codemzs requested review from artidoro and ganikJune 17, 2019 17:11
public TreeEnsembleModelParameters ModelParameters;
};

private TreeEnsembleModelParameters _modelParameters;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

TreeEnsembleModelParameters [](start = 16, length = 27)

Should this be readonly or something similar to make sure it is not altered?

@artidoro

artidoro commented Jun 20, 2019

Copy link
Copy Markdown
Contributor

Since you have already built all this infrastructure why are we not providing the featurizers for LightGbm trainers? I think they still use the same base class for the tree ensemble.
I guess this could come in another PR.

// The 0-1 encoding of leaves the input feature vector falls into.
public float[] Leaves { get; set; }
// The 0-1 encoding of paths the input feature vector reaches the leaves.
public float[] Paths { get; set; }

@artidoroartidoroJun 20, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit (same in other places if you are doing a revision of the PR):
// The 0-1 encoding of paths the input feature vector follows to reach the leaves.

/// and the i-th vector element is the prediction value predicted by the i-th tree.
/// If <see cref="TreesColumnName"/> is <see langword="null"/>, this output column may not be generated.
/// </summary>
public string TreesColumnName;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

TreesColumnName [](start = 26, length = 15)

Suggested renaming:

TreesColumnName -> TreeOutputsColumnName

I think it would be easier to understand, but this is not necessary.

@artidoroartidoro left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

:shipit:

@abgoswamabgoswam left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

:shipit:

@wschin
wschin merged commit 9d29111 into dotnet:masterJun 26, 2019
@wschin
wschin deleted the tree-feat branch June 26, 2019 23:15
Dmitry-A pushed a commit to Dmitry-A/machinelearning that referenced this pull request Jul 24, 2019
* Implement transformer
* Initial draft of porting tree-based featurization
* Internalize something
* Add Tweedie and Ranking cases
* Some small docs
* Customize output column names
* Fix save and load
* Optional output columns
* Fix a test and add some XML docs
* Add samples
* Add a sample
* API docs
* Fix one line
* Add MC test
* Extend a test further
* Address some comments
* Address some comments
* Address comments
* Comment
* Add cache points
* Update test/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs
Co-Authored-By: Justin Ormont <justinormont@users.noreply.github.com>
* Address comment
* Add Justin's test
* Reduce sample size
* Update sample output
@ghostghost locked as resolved and limited conversation to collaborators Mar 21, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TreeEnsembleFeaturizer is not a Transformer yet

6 participants

@wschin@artidoro@Ivanidzo4ka@justinormont@eerhardt@abgoswam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Tree-based featurization - #3812

Merged
wschin merged 27 commits into
dotnet:masterfrom
wschin:tree-feat
Jun 26, 2019
Merged

Tree-based featurization#3812
wschin merged 27 commits into
dotnet:masterfrom
wschin:tree-feat

Conversation

@wschin

@wschinwschin commented Jun 3, 2019

Copy link
Copy Markdown
Contributor

Fix#2482. Generating features using tree structure has been a popular technique in data mining. This PR exposes this internal-only feature to the public.

Since I don't have enough time to handle multiple different assignments at the same time, please don't put nit comments and create new issues instead. Thanks a lot.

@wschinwschin self-assigned this Jun 3, 2019
return new FastForestBinaryTrainer(env, options);
}

public static PretrainedTreeFeaturizationEstimator PretrainTreeEnsembleFeaturizing(this TransformsCatalog catalog,

@eerhardteerhardtJun 3, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

XML Doc on all public classes and APIs. #Resolved

@wschinwschinJun 3, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

No problem. I am working on them. #Resolved

return new PretrainedTreeFeaturizationEstimator(env, options);
}

public static FastForestRegressionFeaturizationEstimator FastForestRegressionFeaturizing(this TransformsCatalog catalog,

@eerhardteerhardtJun 3, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This naming pattern reads a little funny. How about turning it into FeaturizeXXX? Like we have with FeaturizeText. #Resolved

@wschinwschinJun 3, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I can do FeaturizeBy.... #Resolved

@eerhardteerhardtJun 3, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That sounds better (to me at least). #Resolved

/// "Leaves" + <see cref="OutputColumnsSuffix"/>, and "Paths" + <see cref="OutputColumnsSuffix"/>. If <see cref="OutputColumnsSuffix"/>
/// is <see langword="null"/>, the output names would be "Trees", "Leaves", and "Paths".
/// </summary>
public string OutputColumnsSuffix;

@justinormontjustinormontJun 3, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We went away from magic strings in the TextTransform. Previously with tokens=+, we produced a new column named {OutputColName}_TokenizedText.

For the estimators API we have users directly enter the column name for the tokens. We may want to do the same for the Trees/Leaves/Paths of the TreeFeat.

Perhaps:
OutputColumnTreeName, OutputColumnLeavesName, OutputColumnPathsName.

#Resolved

@Ivanidzo4kaIvanidzo4kaJun 4, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

And don't add that column if they empty or equal to null.
That way you can actually configure which parts of tree structure do you want. #Resolved

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

As further background on the PR I was referencing...

Conversation about TextTransform:
via @rogancarr in #2957 PR

When using OutputTokens=true, FeaturizeText creates a new column called ${OutputColumnName}_TransformedText. This isn't really well documented anywhere, and it's odd behavior. I suggest that we make the tokenized text column name explicit in the API.

My suggestion would be the following:

  • Change OutputTokens = [bool] to OutputTokensColumn = [string], and a string.NullOrWhitespace(OutputTokensColumn) signifies that this column will not be created. #Resolved

@wschinwschinJun 5, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Now we can do optional output columns and custom output column names. Please see tests for examples (or wait for formal API samples). #Resolved

/// <param name="catalog">The context <see cref="TransformsCatalog"/> to create <see cref="FastTreeTweedieFeaturizationEstimator"/>.</param>
/// <param name="options">The options to configure <see cref="FastTreeTweedieFeaturizationEstimator"/>. See <see cref="FastTreeTweedieFeaturizationEstimator.Options"/> and
/// <see cref="TreeEnsembleFeaturizationEstimatorBase.CommonOptions"/> for available settings.</param>
public static FastTreeTweedieFeaturizationEstimator FeaturizeByFastTreeTweedie(this TransformsCatalog catalog,

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

May want to note in its name that FastTreeTweedie is regression: (the naming of the others list their task types)

Suggested change
publicstaticFastTreeTweedieFeaturizationEstimatorFeaturizeByFastTreeTweedie(thisTransformsCatalogcatalog,
publicstaticFastTreeTweedieRegressionFeaturizationEstimatorFeaturizeByFastTreeTweedieRegression(thisTransformsCatalogcatalog,
``` #ByDesign

@wschinwschinJun 5, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Yes if the model name doesn't tell the task. Given that Tweedie somehow implies a regression case, we don't have Regression appended to any of public Tweedie modules. This pattern can be seen in FastTreeTweedieTrainer and FastTreeTweedieModelParameters. #Resolved

TrainerOptions = trainerOptions
};

var pipeline = ML.Transforms.FeaturizeByFastTreeBinary(options).

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Creating (8) seperate ML.Transforms.FeaturizeBy{ FastTreeBinary, FastForestRegression, FastTreeRegression, FastTreeTweedie, ... } featurizers under ML.Transforms.* seems a bit large. Seems to clutter up the namespace.

In the future, this list should grow as I think we should have LightGBM variants too (and CatBoost if we take it in). The number of independent featurizers will be:
{ FastTree, FastTreeTweedie, FastForest, LightGBM, CatBoost } x { BinaryClassification, Multiclass, Regression, Ranking }`. (with some combinations missing)

Would it be more clean to have one ML.Transforms.TreeFeaturizer(), and put the specific instance type as a parameter? Would it be doable to have one return type? #ByDesign

@wschinwschinJun 5, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

There are two reasons that I don't like a single TreeFeaturizer.

  1. It goes toward an opposite direction of the C# API's design. Most of them are strongly typed to the underlying data structures. You can see we have a lot of FastTree... and LightGbm..., which is intended.
  2. It may requires user to specify TreeFeaturizer<TTrainer>(options) and the user manually needs to make sure the type of options is TTrainer.Options, which is easy to make mistakes. #Resolved

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You're right, the current pattern will likely have less mistakes by the user.

For the plethora of FastTree... and LightGBM..., those are namespaced under the task mlContext.Regression.Trainers.FastTree(), hence the duplication isn't visible.

TrainerOptions = trainerOptions
};

var pipeline = ML.Transforms.FeaturizeByFastForestRegression(options).

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you add a test of ML.Transforms.FeaturizeByFastForestRegression() using FastForest's ShuffleLabels option? I believe this is the only way to use TreeFeat w/ multi-class classification.

@wschinwschinJun 6, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Let's make it in another PR. This PR has been too large.. #Resolved

@justinormontjustinormontJun 6, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems the UnitTests should be in the main PR. Specifically, I think we should ensure the multi-class case can work. #Resolved

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for adding.
#Resolved

@wschinwschin changed the title [WIP] Tree-based featurizationTree-based featurizationJun 6, 2019
@wschin
wschin requested review from Ivanidzo4ka, eerhardt and justinormont and removed request for Ivanidzo4ka and justinormontJune 6, 2019 17:58
Comment threadsrc/Microsoft.ML.FastTree/TreeEnsembleFeaturizer.cs
@wschin
wschin requested a review from eerhardtJune 6, 2019 22:10
Comment threadtest/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs Outdated
Comment threadtest/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs Outdated
}

[Fact]
public void TreeEnsembleFeaturizingPipelineMulticlass()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@daholste and I were able to map the Key to a float using a CustomMapping().

I'd recommend the multiclass unit test be:

Suggested change
publicvoidTreeEnsembleFeaturizingPipelineMulticlass()
[Fact]
publicvoidTreeEnsembleFeaturizingPipelineMulticlass()
{
intdataPointCount=1000;
vardata=SamplesUtils.DatasetUtils.GenerateRandomMulticlassClassificationExamples(dataPointCount).ToList();
vardataView=ML.Data.LoadFromEnumerable(data);
dataView=ML.Data.Cache(dataView);
vartrainerOptions=newFastForestRegressionTrainer.Options
{
NumberOfThreads=1,
NumberOfTrees=10,
NumberOfLeaves=4,
MinimumExampleCountPerLeaf=10,
FeatureColumnName="Features",
LabelColumnName="FloatLabel",
ShuffleLabels=true
};
varoptions=newFastForestRegressionFeaturizationEstimator.Options()
{
InputColumnName="Features",
TreesColumnName="Trees",
LeavesColumnName="Leaves",
PathsColumnName="Paths",
TrainerOptions= trainerOptions
};
Action<RowWithKey,RowWithFloat>actionConvertKeyToFloat=(RowWithKeyrowWithKey,RowWithFloatrowWithFloat)=>
{
rowWithFloat.FloatLabel=rowWithKey.KeyLabel==0?float.NaN:rowWithKey.KeyLabel-1;
};
varsplit=ML.Data.TrainTestSplit(dataView,0.5);
vartrainData=split.TrainSet;
vartestData=split.TestSet;
varpipeline=ML.Transforms.Conversion.MapValueToKey("KeyLabel","Label")
.Append(ML.Transforms.CustomMapping(actionConvertKeyToFloat,"KeyLabel"))
.Append(ML.Transforms.FeaturizeByFastForestRegression(options))
.Append(ML.Transforms.Concatenate("CombinedFeatures","Trees","Leaves","Paths"))
.Append(ML.MulticlassClassification.Trainers.SdcaMaximumEntropy("KeyLabel","CombinedFeatures"));
varmodel=pipeline.Fit(trainData);
varprediction=model.Transform(testData);
varmetrics=ML.MulticlassClassification.Evaluate(prediction,labelColumnName:"KeyLabel");
Assert.True(metrics.MacroAccuracy>0.6);
Assert.True(metrics.MicroAccuracy>0.6);
}
class RowWithKey
{
[KeyType()]
publicuintKeyLabel{get;set;}
}
class RowWithFloat
{
publicfloatFloatLabel{get;set;}
}

Specifically, this is using a CustomMapping() to convert the Key to a float for use in the TreeFeat's FastForest regression. The current method in the unit test requires a user to know/list all of the values in their Label (and their type). The CustomMapping() style is easier for a user to replicate for their dataset.

We also added a TrainTestSplit() so we're not testing on the training set, and we removed the original features from the Concatenate() to ensure the TreeFeat's output features are useful.

@codemzs
codemzs requested review from artidoro and ganikJune 17, 2019 17:11
public TreeEnsembleModelParameters ModelParameters;
};

private TreeEnsembleModelParameters _modelParameters;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

TreeEnsembleModelParameters [](start = 16, length = 27)

Should this be readonly or something similar to make sure it is not altered?

@artidoro

artidoro commented Jun 20, 2019

Copy link
Copy Markdown
Contributor

Since you have already built all this infrastructure why are we not providing the featurizers for LightGbm trainers? I think they still use the same base class for the tree ensemble.
I guess this could come in another PR.

// The 0-1 encoding of leaves the input feature vector falls into.
public float[] Leaves { get; set; }
// The 0-1 encoding of paths the input feature vector reaches the leaves.
public float[] Paths { get; set; }

@artidoroartidoroJun 20, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit (same in other places if you are doing a revision of the PR):
// The 0-1 encoding of paths the input feature vector follows to reach the leaves.

/// and the i-th vector element is the prediction value predicted by the i-th tree.
/// If <see cref="TreesColumnName"/> is <see langword="null"/>, this output column may not be generated.
/// </summary>
public string TreesColumnName;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

TreesColumnName [](start = 26, length = 15)

Suggested renaming:

TreesColumnName -> TreeOutputsColumnName

I think it would be easier to understand, but this is not necessary.

@artidoroartidoro left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

:shipit:

@abgoswamabgoswam left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

:shipit:

@wschin
wschin merged commit 9d29111 into dotnet:masterJun 26, 2019
@wschin
wschin deleted the tree-feat branch June 26, 2019 23:15
Dmitry-A pushed a commit to Dmitry-A/machinelearning that referenced this pull request Jul 24, 2019
* Implement transformer
* Initial draft of porting tree-based featurization
* Internalize something
* Add Tweedie and Ranking cases
* Some small docs
* Customize output column names
* Fix save and load
* Optional output columns
* Fix a test and add some XML docs
* Add samples
* Add a sample
* API docs
* Fix one line
* Add MC test
* Extend a test further
* Address some comments
* Address some comments
* Address comments
* Comment
* Add cache points
* Update test/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs
Co-Authored-By: Justin Ormont <justinormont@users.noreply.github.com>
* Address comment
* Add Justin's test
* Reduce sample size
* Update sample output
@ghostghost locked as resolved and limited conversation to collaborators Mar 21, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TreeEnsembleFeaturizer is not a Transformer yet

6 participants

@wschin@artidoro@Ivanidzo4ka@justinormont@eerhardt@abgoswam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Tree-based featurization - #3812

Merged
wschin merged 27 commits into
dotnet:masterfrom
wschin:tree-feat
Jun 26, 2019
Merged

Tree-based featurization#3812
wschin merged 27 commits into
dotnet:masterfrom
wschin:tree-feat

Conversation

@wschin

@wschinwschin commented Jun 3, 2019

Copy link
Copy Markdown
Contributor

Fix#2482. Generating features using tree structure has been a popular technique in data mining. This PR exposes this internal-only feature to the public.

Since I don't have enough time to handle multiple different assignments at the same time, please don't put nit comments and create new issues instead. Thanks a lot.

@wschinwschin self-assigned this Jun 3, 2019
return new FastForestBinaryTrainer(env, options);
}

public static PretrainedTreeFeaturizationEstimator PretrainTreeEnsembleFeaturizing(this TransformsCatalog catalog,

@eerhardteerhardtJun 3, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

XML Doc on all public classes and APIs. #Resolved

@wschinwschinJun 3, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

No problem. I am working on them. #Resolved

return new PretrainedTreeFeaturizationEstimator(env, options);
}

public static FastForestRegressionFeaturizationEstimator FastForestRegressionFeaturizing(this TransformsCatalog catalog,

@eerhardteerhardtJun 3, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This naming pattern reads a little funny. How about turning it into FeaturizeXXX? Like we have with FeaturizeText. #Resolved

@wschinwschinJun 3, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I can do FeaturizeBy.... #Resolved

@eerhardteerhardtJun 3, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That sounds better (to me at least). #Resolved

/// "Leaves" + <see cref="OutputColumnsSuffix"/>, and "Paths" + <see cref="OutputColumnsSuffix"/>. If <see cref="OutputColumnsSuffix"/>
/// is <see langword="null"/>, the output names would be "Trees", "Leaves", and "Paths".
/// </summary>
public string OutputColumnsSuffix;

@justinormontjustinormontJun 3, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We went away from magic strings in the TextTransform. Previously with tokens=+, we produced a new column named {OutputColName}_TokenizedText.

For the estimators API we have users directly enter the column name for the tokens. We may want to do the same for the Trees/Leaves/Paths of the TreeFeat.

Perhaps:
OutputColumnTreeName, OutputColumnLeavesName, OutputColumnPathsName.

#Resolved

@Ivanidzo4kaIvanidzo4kaJun 4, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

And don't add that column if they empty or equal to null.
That way you can actually configure which parts of tree structure do you want. #Resolved

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

As further background on the PR I was referencing...

Conversation about TextTransform:
via @rogancarr in #2957 PR

When using OutputTokens=true, FeaturizeText creates a new column called ${OutputColumnName}_TransformedText. This isn't really well documented anywhere, and it's odd behavior. I suggest that we make the tokenized text column name explicit in the API.

My suggestion would be the following:

  • Change OutputTokens = [bool] to OutputTokensColumn = [string], and a string.NullOrWhitespace(OutputTokensColumn) signifies that this column will not be created. #Resolved

@wschinwschinJun 5, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Now we can do optional output columns and custom output column names. Please see tests for examples (or wait for formal API samples). #Resolved

/// <param name="catalog">The context <see cref="TransformsCatalog"/> to create <see cref="FastTreeTweedieFeaturizationEstimator"/>.</param>
/// <param name="options">The options to configure <see cref="FastTreeTweedieFeaturizationEstimator"/>. See <see cref="FastTreeTweedieFeaturizationEstimator.Options"/> and
/// <see cref="TreeEnsembleFeaturizationEstimatorBase.CommonOptions"/> for available settings.</param>
public static FastTreeTweedieFeaturizationEstimator FeaturizeByFastTreeTweedie(this TransformsCatalog catalog,

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

May want to note in its name that FastTreeTweedie is regression: (the naming of the others list their task types)

Suggested change
publicstaticFastTreeTweedieFeaturizationEstimatorFeaturizeByFastTreeTweedie(thisTransformsCatalogcatalog,
publicstaticFastTreeTweedieRegressionFeaturizationEstimatorFeaturizeByFastTreeTweedieRegression(thisTransformsCatalogcatalog,
``` #ByDesign

@wschinwschinJun 5, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Yes if the model name doesn't tell the task. Given that Tweedie somehow implies a regression case, we don't have Regression appended to any of public Tweedie modules. This pattern can be seen in FastTreeTweedieTrainer and FastTreeTweedieModelParameters. #Resolved

TrainerOptions = trainerOptions
};

var pipeline = ML.Transforms.FeaturizeByFastTreeBinary(options).

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Creating (8) seperate ML.Transforms.FeaturizeBy{ FastTreeBinary, FastForestRegression, FastTreeRegression, FastTreeTweedie, ... } featurizers under ML.Transforms.* seems a bit large. Seems to clutter up the namespace.

In the future, this list should grow as I think we should have LightGBM variants too (and CatBoost if we take it in). The number of independent featurizers will be:
{ FastTree, FastTreeTweedie, FastForest, LightGBM, CatBoost } x { BinaryClassification, Multiclass, Regression, Ranking }`. (with some combinations missing)

Would it be more clean to have one ML.Transforms.TreeFeaturizer(), and put the specific instance type as a parameter? Would it be doable to have one return type? #ByDesign

@wschinwschinJun 5, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

There are two reasons that I don't like a single TreeFeaturizer.

  1. It goes toward an opposite direction of the C# API's design. Most of them are strongly typed to the underlying data structures. You can see we have a lot of FastTree... and LightGbm..., which is intended.
  2. It may requires user to specify TreeFeaturizer<TTrainer>(options) and the user manually needs to make sure the type of options is TTrainer.Options, which is easy to make mistakes. #Resolved

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You're right, the current pattern will likely have less mistakes by the user.

For the plethora of FastTree... and LightGBM..., those are namespaced under the task mlContext.Regression.Trainers.FastTree(), hence the duplication isn't visible.

TrainerOptions = trainerOptions
};

var pipeline = ML.Transforms.FeaturizeByFastForestRegression(options).

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you add a test of ML.Transforms.FeaturizeByFastForestRegression() using FastForest's ShuffleLabels option? I believe this is the only way to use TreeFeat w/ multi-class classification.

@wschinwschinJun 6, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Let's make it in another PR. This PR has been too large.. #Resolved

@justinormontjustinormontJun 6, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems the UnitTests should be in the main PR. Specifically, I think we should ensure the multi-class case can work. #Resolved

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for adding.
#Resolved

@wschinwschin changed the title [WIP] Tree-based featurizationTree-based featurizationJun 6, 2019
@wschin
wschin requested review from Ivanidzo4ka, eerhardt and justinormont and removed request for Ivanidzo4ka and justinormontJune 6, 2019 17:58
Comment threadsrc/Microsoft.ML.FastTree/TreeEnsembleFeaturizer.cs
@wschin
wschin requested a review from eerhardtJune 6, 2019 22:10
Comment threadtest/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs Outdated
Comment threadtest/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs Outdated
}

[Fact]
public void TreeEnsembleFeaturizingPipelineMulticlass()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@daholste and I were able to map the Key to a float using a CustomMapping().

I'd recommend the multiclass unit test be:

Suggested change
publicvoidTreeEnsembleFeaturizingPipelineMulticlass()
[Fact]
publicvoidTreeEnsembleFeaturizingPipelineMulticlass()
{
intdataPointCount=1000;
vardata=SamplesUtils.DatasetUtils.GenerateRandomMulticlassClassificationExamples(dataPointCount).ToList();
vardataView=ML.Data.LoadFromEnumerable(data);
dataView=ML.Data.Cache(dataView);
vartrainerOptions=newFastForestRegressionTrainer.Options
{
NumberOfThreads=1,
NumberOfTrees=10,
NumberOfLeaves=4,
MinimumExampleCountPerLeaf=10,
FeatureColumnName="Features",
LabelColumnName="FloatLabel",
ShuffleLabels=true
};
varoptions=newFastForestRegressionFeaturizationEstimator.Options()
{
InputColumnName="Features",
TreesColumnName="Trees",
LeavesColumnName="Leaves",
PathsColumnName="Paths",
TrainerOptions= trainerOptions
};
Action<RowWithKey,RowWithFloat>actionConvertKeyToFloat=(RowWithKeyrowWithKey,RowWithFloatrowWithFloat)=>
{
rowWithFloat.FloatLabel=rowWithKey.KeyLabel==0?float.NaN:rowWithKey.KeyLabel-1;
};
varsplit=ML.Data.TrainTestSplit(dataView,0.5);
vartrainData=split.TrainSet;
vartestData=split.TestSet;
varpipeline=ML.Transforms.Conversion.MapValueToKey("KeyLabel","Label")
.Append(ML.Transforms.CustomMapping(actionConvertKeyToFloat,"KeyLabel"))
.Append(ML.Transforms.FeaturizeByFastForestRegression(options))
.Append(ML.Transforms.Concatenate("CombinedFeatures","Trees","Leaves","Paths"))
.Append(ML.MulticlassClassification.Trainers.SdcaMaximumEntropy("KeyLabel","CombinedFeatures"));
varmodel=pipeline.Fit(trainData);
varprediction=model.Transform(testData);
varmetrics=ML.MulticlassClassification.Evaluate(prediction,labelColumnName:"KeyLabel");
Assert.True(metrics.MacroAccuracy>0.6);
Assert.True(metrics.MicroAccuracy>0.6);
}
class RowWithKey
{
[KeyType()]
publicuintKeyLabel{get;set;}
}
class RowWithFloat
{
publicfloatFloatLabel{get;set;}
}

Specifically, this is using a CustomMapping() to convert the Key to a float for use in the TreeFeat's FastForest regression. The current method in the unit test requires a user to know/list all of the values in their Label (and their type). The CustomMapping() style is easier for a user to replicate for their dataset.

We also added a TrainTestSplit() so we're not testing on the training set, and we removed the original features from the Concatenate() to ensure the TreeFeat's output features are useful.

@codemzs
codemzs requested review from artidoro and ganikJune 17, 2019 17:11
public TreeEnsembleModelParameters ModelParameters;
};

private TreeEnsembleModelParameters _modelParameters;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

TreeEnsembleModelParameters [](start = 16, length = 27)

Should this be readonly or something similar to make sure it is not altered?

@artidoro

artidoro commented Jun 20, 2019

Copy link
Copy Markdown
Contributor

Since you have already built all this infrastructure why are we not providing the featurizers for LightGbm trainers? I think they still use the same base class for the tree ensemble.
I guess this could come in another PR.

// The 0-1 encoding of leaves the input feature vector falls into.
public float[] Leaves { get; set; }
// The 0-1 encoding of paths the input feature vector reaches the leaves.
public float[] Paths { get; set; }

@artidoroartidoroJun 20, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit (same in other places if you are doing a revision of the PR):
// The 0-1 encoding of paths the input feature vector follows to reach the leaves.

/// and the i-th vector element is the prediction value predicted by the i-th tree.
/// If <see cref="TreesColumnName"/> is <see langword="null"/>, this output column may not be generated.
/// </summary>
public string TreesColumnName;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

TreesColumnName [](start = 26, length = 15)

Suggested renaming:

TreesColumnName -> TreeOutputsColumnName

I think it would be easier to understand, but this is not necessary.

@artidoroartidoro left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

:shipit:

@abgoswamabgoswam left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

:shipit:

@wschin
wschin merged commit 9d29111 into dotnet:masterJun 26, 2019
@wschin
wschin deleted the tree-feat branch June 26, 2019 23:15
Dmitry-A pushed a commit to Dmitry-A/machinelearning that referenced this pull request Jul 24, 2019
* Implement transformer
* Initial draft of porting tree-based featurization
* Internalize something
* Add Tweedie and Ranking cases
* Some small docs
* Customize output column names
* Fix save and load
* Optional output columns
* Fix a test and add some XML docs
* Add samples
* Add a sample
* API docs
* Fix one line
* Add MC test
* Extend a test further
* Address some comments
* Address some comments
* Address comments
* Comment
* Add cache points
* Update test/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs
Co-Authored-By: Justin Ormont <justinormont@users.noreply.github.com>
* Address comment
* Add Justin's test
* Reduce sample size
* Update sample output
@ghostghost locked as resolved and limited conversation to collaborators Mar 21, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TreeEnsembleFeaturizer is not a Transformer yet

6 participants

@wschin@artidoro@Ivanidzo4ka@justinormont@eerhardt@abgoswam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Tree-based featurization - #3812

Merged
wschin merged 27 commits into
dotnet:masterfrom
wschin:tree-feat
Jun 26, 2019
Merged

Tree-based featurization#3812
wschin merged 27 commits into
dotnet:masterfrom
wschin:tree-feat

Conversation

@wschin

@wschinwschin commented Jun 3, 2019

Copy link
Copy Markdown
Contributor

Fix#2482. Generating features using tree structure has been a popular technique in data mining. This PR exposes this internal-only feature to the public.

Since I don't have enough time to handle multiple different assignments at the same time, please don't put nit comments and create new issues instead. Thanks a lot.

@wschinwschin self-assigned this Jun 3, 2019
return new FastForestBinaryTrainer(env, options);
}

public static PretrainedTreeFeaturizationEstimator PretrainTreeEnsembleFeaturizing(this TransformsCatalog catalog,

@eerhardteerhardtJun 3, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

XML Doc on all public classes and APIs. #Resolved

@wschinwschinJun 3, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

No problem. I am working on them. #Resolved

return new PretrainedTreeFeaturizationEstimator(env, options);
}

public static FastForestRegressionFeaturizationEstimator FastForestRegressionFeaturizing(this TransformsCatalog catalog,

@eerhardteerhardtJun 3, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This naming pattern reads a little funny. How about turning it into FeaturizeXXX? Like we have with FeaturizeText. #Resolved

@wschinwschinJun 3, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I can do FeaturizeBy.... #Resolved

@eerhardteerhardtJun 3, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That sounds better (to me at least). #Resolved

/// "Leaves" + <see cref="OutputColumnsSuffix"/>, and "Paths" + <see cref="OutputColumnsSuffix"/>. If <see cref="OutputColumnsSuffix"/>
/// is <see langword="null"/>, the output names would be "Trees", "Leaves", and "Paths".
/// </summary>
public string OutputColumnsSuffix;

@justinormontjustinormontJun 3, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We went away from magic strings in the TextTransform. Previously with tokens=+, we produced a new column named {OutputColName}_TokenizedText.

For the estimators API we have users directly enter the column name for the tokens. We may want to do the same for the Trees/Leaves/Paths of the TreeFeat.

Perhaps:
OutputColumnTreeName, OutputColumnLeavesName, OutputColumnPathsName.

#Resolved

@Ivanidzo4kaIvanidzo4kaJun 4, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

And don't add that column if they empty or equal to null.
That way you can actually configure which parts of tree structure do you want. #Resolved

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

As further background on the PR I was referencing...

Conversation about TextTransform:
via @rogancarr in #2957 PR

When using OutputTokens=true, FeaturizeText creates a new column called ${OutputColumnName}_TransformedText. This isn't really well documented anywhere, and it's odd behavior. I suggest that we make the tokenized text column name explicit in the API.

My suggestion would be the following:

  • Change OutputTokens = [bool] to OutputTokensColumn = [string], and a string.NullOrWhitespace(OutputTokensColumn) signifies that this column will not be created. #Resolved

@wschinwschinJun 5, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Now we can do optional output columns and custom output column names. Please see tests for examples (or wait for formal API samples). #Resolved

/// <param name="catalog">The context <see cref="TransformsCatalog"/> to create <see cref="FastTreeTweedieFeaturizationEstimator"/>.</param>
/// <param name="options">The options to configure <see cref="FastTreeTweedieFeaturizationEstimator"/>. See <see cref="FastTreeTweedieFeaturizationEstimator.Options"/> and
/// <see cref="TreeEnsembleFeaturizationEstimatorBase.CommonOptions"/> for available settings.</param>
public static FastTreeTweedieFeaturizationEstimator FeaturizeByFastTreeTweedie(this TransformsCatalog catalog,

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

May want to note in its name that FastTreeTweedie is regression: (the naming of the others list their task types)

Suggested change
publicstaticFastTreeTweedieFeaturizationEstimatorFeaturizeByFastTreeTweedie(thisTransformsCatalogcatalog,
publicstaticFastTreeTweedieRegressionFeaturizationEstimatorFeaturizeByFastTreeTweedieRegression(thisTransformsCatalogcatalog,
``` #ByDesign

@wschinwschinJun 5, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Yes if the model name doesn't tell the task. Given that Tweedie somehow implies a regression case, we don't have Regression appended to any of public Tweedie modules. This pattern can be seen in FastTreeTweedieTrainer and FastTreeTweedieModelParameters. #Resolved

TrainerOptions = trainerOptions
};

var pipeline = ML.Transforms.FeaturizeByFastTreeBinary(options).

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Creating (8) seperate ML.Transforms.FeaturizeBy{ FastTreeBinary, FastForestRegression, FastTreeRegression, FastTreeTweedie, ... } featurizers under ML.Transforms.* seems a bit large. Seems to clutter up the namespace.

In the future, this list should grow as I think we should have LightGBM variants too (and CatBoost if we take it in). The number of independent featurizers will be:
{ FastTree, FastTreeTweedie, FastForest, LightGBM, CatBoost } x { BinaryClassification, Multiclass, Regression, Ranking }`. (with some combinations missing)

Would it be more clean to have one ML.Transforms.TreeFeaturizer(), and put the specific instance type as a parameter? Would it be doable to have one return type? #ByDesign

@wschinwschinJun 5, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

There are two reasons that I don't like a single TreeFeaturizer.

  1. It goes toward an opposite direction of the C# API's design. Most of them are strongly typed to the underlying data structures. You can see we have a lot of FastTree... and LightGbm..., which is intended.
  2. It may requires user to specify TreeFeaturizer<TTrainer>(options) and the user manually needs to make sure the type of options is TTrainer.Options, which is easy to make mistakes. #Resolved

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You're right, the current pattern will likely have less mistakes by the user.

For the plethora of FastTree... and LightGBM..., those are namespaced under the task mlContext.Regression.Trainers.FastTree(), hence the duplication isn't visible.

TrainerOptions = trainerOptions
};

var pipeline = ML.Transforms.FeaturizeByFastForestRegression(options).

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you add a test of ML.Transforms.FeaturizeByFastForestRegression() using FastForest's ShuffleLabels option? I believe this is the only way to use TreeFeat w/ multi-class classification.

@wschinwschinJun 6, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Let's make it in another PR. This PR has been too large.. #Resolved

@justinormontjustinormontJun 6, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems the UnitTests should be in the main PR. Specifically, I think we should ensure the multi-class case can work. #Resolved

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for adding.
#Resolved

@wschinwschin changed the title [WIP] Tree-based featurizationTree-based featurizationJun 6, 2019
@wschin
wschin requested review from Ivanidzo4ka, eerhardt and justinormont and removed request for Ivanidzo4ka and justinormontJune 6, 2019 17:58
Comment threadsrc/Microsoft.ML.FastTree/TreeEnsembleFeaturizer.cs
@wschin
wschin requested a review from eerhardtJune 6, 2019 22:10
Comment threadtest/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs Outdated
Comment threadtest/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs Outdated
}

[Fact]
public void TreeEnsembleFeaturizingPipelineMulticlass()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@daholste and I were able to map the Key to a float using a CustomMapping().

I'd recommend the multiclass unit test be:

Suggested change
publicvoidTreeEnsembleFeaturizingPipelineMulticlass()
[Fact]
publicvoidTreeEnsembleFeaturizingPipelineMulticlass()
{
intdataPointCount=1000;
vardata=SamplesUtils.DatasetUtils.GenerateRandomMulticlassClassificationExamples(dataPointCount).ToList();
vardataView=ML.Data.LoadFromEnumerable(data);
dataView=ML.Data.Cache(dataView);
vartrainerOptions=newFastForestRegressionTrainer.Options
{
NumberOfThreads=1,
NumberOfTrees=10,
NumberOfLeaves=4,
MinimumExampleCountPerLeaf=10,
FeatureColumnName="Features",
LabelColumnName="FloatLabel",
ShuffleLabels=true
};
varoptions=newFastForestRegressionFeaturizationEstimator.Options()
{
InputColumnName="Features",
TreesColumnName="Trees",
LeavesColumnName="Leaves",
PathsColumnName="Paths",
TrainerOptions= trainerOptions
};
Action<RowWithKey,RowWithFloat>actionConvertKeyToFloat=(RowWithKeyrowWithKey,RowWithFloatrowWithFloat)=>
{
rowWithFloat.FloatLabel=rowWithKey.KeyLabel==0?float.NaN:rowWithKey.KeyLabel-1;
};
varsplit=ML.Data.TrainTestSplit(dataView,0.5);
vartrainData=split.TrainSet;
vartestData=split.TestSet;
varpipeline=ML.Transforms.Conversion.MapValueToKey("KeyLabel","Label")
.Append(ML.Transforms.CustomMapping(actionConvertKeyToFloat,"KeyLabel"))
.Append(ML.Transforms.FeaturizeByFastForestRegression(options))
.Append(ML.Transforms.Concatenate("CombinedFeatures","Trees","Leaves","Paths"))
.Append(ML.MulticlassClassification.Trainers.SdcaMaximumEntropy("KeyLabel","CombinedFeatures"));
varmodel=pipeline.Fit(trainData);
varprediction=model.Transform(testData);
varmetrics=ML.MulticlassClassification.Evaluate(prediction,labelColumnName:"KeyLabel");
Assert.True(metrics.MacroAccuracy>0.6);
Assert.True(metrics.MicroAccuracy>0.6);
}
class RowWithKey
{
[KeyType()]
publicuintKeyLabel{get;set;}
}
class RowWithFloat
{
publicfloatFloatLabel{get;set;}
}

Specifically, this is using a CustomMapping() to convert the Key to a float for use in the TreeFeat's FastForest regression. The current method in the unit test requires a user to know/list all of the values in their Label (and their type). The CustomMapping() style is easier for a user to replicate for their dataset.

We also added a TrainTestSplit() so we're not testing on the training set, and we removed the original features from the Concatenate() to ensure the TreeFeat's output features are useful.

@codemzs
codemzs requested review from artidoro and ganikJune 17, 2019 17:11
public TreeEnsembleModelParameters ModelParameters;
};

private TreeEnsembleModelParameters _modelParameters;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

TreeEnsembleModelParameters [](start = 16, length = 27)

Should this be readonly or something similar to make sure it is not altered?

@artidoro

artidoro commented Jun 20, 2019

Copy link
Copy Markdown
Contributor

Since you have already built all this infrastructure why are we not providing the featurizers for LightGbm trainers? I think they still use the same base class for the tree ensemble.
I guess this could come in another PR.

// The 0-1 encoding of leaves the input feature vector falls into.
public float[] Leaves { get; set; }
// The 0-1 encoding of paths the input feature vector reaches the leaves.
public float[] Paths { get; set; }

@artidoroartidoroJun 20, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit (same in other places if you are doing a revision of the PR):
// The 0-1 encoding of paths the input feature vector follows to reach the leaves.

/// and the i-th vector element is the prediction value predicted by the i-th tree.
/// If <see cref="TreesColumnName"/> is <see langword="null"/>, this output column may not be generated.
/// </summary>
public string TreesColumnName;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

TreesColumnName [](start = 26, length = 15)

Suggested renaming:

TreesColumnName -> TreeOutputsColumnName

I think it would be easier to understand, but this is not necessary.

@artidoroartidoro left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

:shipit:

@abgoswamabgoswam left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

:shipit:

@wschin
wschin merged commit 9d29111 into dotnet:masterJun 26, 2019
@wschin
wschin deleted the tree-feat branch June 26, 2019 23:15
Dmitry-A pushed a commit to Dmitry-A/machinelearning that referenced this pull request Jul 24, 2019
* Implement transformer
* Initial draft of porting tree-based featurization
* Internalize something
* Add Tweedie and Ranking cases
* Some small docs
* Customize output column names
* Fix save and load
* Optional output columns
* Fix a test and add some XML docs
* Add samples
* Add a sample
* API docs
* Fix one line
* Add MC test
* Extend a test further
* Address some comments
* Address some comments
* Address comments
* Comment
* Add cache points
* Update test/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs
Co-Authored-By: Justin Ormont <justinormont@users.noreply.github.com>
* Address comment
* Add Justin's test
* Reduce sample size
* Update sample output
@ghostghost locked as resolved and limited conversation to collaborators Mar 21, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TreeEnsembleFeaturizer is not a Transformer yet

6 participants

@wschin@artidoro@Ivanidzo4ka@justinormont@eerhardt@abgoswam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Tree-based featurization - #3812

Merged
wschin merged 27 commits into
dotnet:masterfrom
wschin:tree-feat
Jun 26, 2019
Merged

Tree-based featurization#3812
wschin merged 27 commits into
dotnet:masterfrom
wschin:tree-feat

Conversation

@wschin

@wschinwschin commented Jun 3, 2019

Copy link
Copy Markdown
Contributor

Fix#2482. Generating features using tree structure has been a popular technique in data mining. This PR exposes this internal-only feature to the public.

Since I don't have enough time to handle multiple different assignments at the same time, please don't put nit comments and create new issues instead. Thanks a lot.

@wschinwschin self-assigned this Jun 3, 2019
return new FastForestBinaryTrainer(env, options);
}

public static PretrainedTreeFeaturizationEstimator PretrainTreeEnsembleFeaturizing(this TransformsCatalog catalog,

@eerhardteerhardtJun 3, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

XML Doc on all public classes and APIs. #Resolved

@wschinwschinJun 3, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

No problem. I am working on them. #Resolved

return new PretrainedTreeFeaturizationEstimator(env, options);
}

public static FastForestRegressionFeaturizationEstimator FastForestRegressionFeaturizing(this TransformsCatalog catalog,

@eerhardteerhardtJun 3, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This naming pattern reads a little funny. How about turning it into FeaturizeXXX? Like we have with FeaturizeText. #Resolved

@wschinwschinJun 3, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I can do FeaturizeBy.... #Resolved

@eerhardteerhardtJun 3, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That sounds better (to me at least). #Resolved

/// "Leaves" + <see cref="OutputColumnsSuffix"/>, and "Paths" + <see cref="OutputColumnsSuffix"/>. If <see cref="OutputColumnsSuffix"/>
/// is <see langword="null"/>, the output names would be "Trees", "Leaves", and "Paths".
/// </summary>
public string OutputColumnsSuffix;

@justinormontjustinormontJun 3, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We went away from magic strings in the TextTransform. Previously with tokens=+, we produced a new column named {OutputColName}_TokenizedText.

For the estimators API we have users directly enter the column name for the tokens. We may want to do the same for the Trees/Leaves/Paths of the TreeFeat.

Perhaps:
OutputColumnTreeName, OutputColumnLeavesName, OutputColumnPathsName.

#Resolved

@Ivanidzo4kaIvanidzo4kaJun 4, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

And don't add that column if they empty or equal to null.
That way you can actually configure which parts of tree structure do you want. #Resolved

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

As further background on the PR I was referencing...

Conversation about TextTransform:
via @rogancarr in #2957 PR

When using OutputTokens=true, FeaturizeText creates a new column called ${OutputColumnName}_TransformedText. This isn't really well documented anywhere, and it's odd behavior. I suggest that we make the tokenized text column name explicit in the API.

My suggestion would be the following:

  • Change OutputTokens = [bool] to OutputTokensColumn = [string], and a string.NullOrWhitespace(OutputTokensColumn) signifies that this column will not be created. #Resolved

@wschinwschinJun 5, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Now we can do optional output columns and custom output column names. Please see tests for examples (or wait for formal API samples). #Resolved

/// <param name="catalog">The context <see cref="TransformsCatalog"/> to create <see cref="FastTreeTweedieFeaturizationEstimator"/>.</param>
/// <param name="options">The options to configure <see cref="FastTreeTweedieFeaturizationEstimator"/>. See <see cref="FastTreeTweedieFeaturizationEstimator.Options"/> and
/// <see cref="TreeEnsembleFeaturizationEstimatorBase.CommonOptions"/> for available settings.</param>
public static FastTreeTweedieFeaturizationEstimator FeaturizeByFastTreeTweedie(this TransformsCatalog catalog,

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

May want to note in its name that FastTreeTweedie is regression: (the naming of the others list their task types)

Suggested change
publicstaticFastTreeTweedieFeaturizationEstimatorFeaturizeByFastTreeTweedie(thisTransformsCatalogcatalog,
publicstaticFastTreeTweedieRegressionFeaturizationEstimatorFeaturizeByFastTreeTweedieRegression(thisTransformsCatalogcatalog,
``` #ByDesign

@wschinwschinJun 5, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Yes if the model name doesn't tell the task. Given that Tweedie somehow implies a regression case, we don't have Regression appended to any of public Tweedie modules. This pattern can be seen in FastTreeTweedieTrainer and FastTreeTweedieModelParameters. #Resolved

TrainerOptions = trainerOptions
};

var pipeline = ML.Transforms.FeaturizeByFastTreeBinary(options).

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Creating (8) seperate ML.Transforms.FeaturizeBy{ FastTreeBinary, FastForestRegression, FastTreeRegression, FastTreeTweedie, ... } featurizers under ML.Transforms.* seems a bit large. Seems to clutter up the namespace.

In the future, this list should grow as I think we should have LightGBM variants too (and CatBoost if we take it in). The number of independent featurizers will be:
{ FastTree, FastTreeTweedie, FastForest, LightGBM, CatBoost } x { BinaryClassification, Multiclass, Regression, Ranking }`. (with some combinations missing)

Would it be more clean to have one ML.Transforms.TreeFeaturizer(), and put the specific instance type as a parameter? Would it be doable to have one return type? #ByDesign

@wschinwschinJun 5, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

There are two reasons that I don't like a single TreeFeaturizer.

  1. It goes toward an opposite direction of the C# API's design. Most of them are strongly typed to the underlying data structures. You can see we have a lot of FastTree... and LightGbm..., which is intended.
  2. It may requires user to specify TreeFeaturizer<TTrainer>(options) and the user manually needs to make sure the type of options is TTrainer.Options, which is easy to make mistakes. #Resolved

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You're right, the current pattern will likely have less mistakes by the user.

For the plethora of FastTree... and LightGBM..., those are namespaced under the task mlContext.Regression.Trainers.FastTree(), hence the duplication isn't visible.

TrainerOptions = trainerOptions
};

var pipeline = ML.Transforms.FeaturizeByFastForestRegression(options).

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you add a test of ML.Transforms.FeaturizeByFastForestRegression() using FastForest's ShuffleLabels option? I believe this is the only way to use TreeFeat w/ multi-class classification.

@wschinwschinJun 6, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Let's make it in another PR. This PR has been too large.. #Resolved

@justinormontjustinormontJun 6, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems the UnitTests should be in the main PR. Specifically, I think we should ensure the multi-class case can work. #Resolved

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for adding.
#Resolved

@wschinwschin changed the title [WIP] Tree-based featurizationTree-based featurizationJun 6, 2019
@wschin
wschin requested review from Ivanidzo4ka, eerhardt and justinormont and removed request for Ivanidzo4ka and justinormontJune 6, 2019 17:58
Comment threadsrc/Microsoft.ML.FastTree/TreeEnsembleFeaturizer.cs
@wschin
wschin requested a review from eerhardtJune 6, 2019 22:10
Comment threadtest/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs Outdated
Comment threadtest/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs Outdated
}

[Fact]
public void TreeEnsembleFeaturizingPipelineMulticlass()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@daholste and I were able to map the Key to a float using a CustomMapping().

I'd recommend the multiclass unit test be:

Suggested change
publicvoidTreeEnsembleFeaturizingPipelineMulticlass()
[Fact]
publicvoidTreeEnsembleFeaturizingPipelineMulticlass()
{
intdataPointCount=1000;
vardata=SamplesUtils.DatasetUtils.GenerateRandomMulticlassClassificationExamples(dataPointCount).ToList();
vardataView=ML.Data.LoadFromEnumerable(data);
dataView=ML.Data.Cache(dataView);
vartrainerOptions=newFastForestRegressionTrainer.Options
{
NumberOfThreads=1,
NumberOfTrees=10,
NumberOfLeaves=4,
MinimumExampleCountPerLeaf=10,
FeatureColumnName="Features",
LabelColumnName="FloatLabel",
ShuffleLabels=true
};
varoptions=newFastForestRegressionFeaturizationEstimator.Options()
{
InputColumnName="Features",
TreesColumnName="Trees",
LeavesColumnName="Leaves",
PathsColumnName="Paths",
TrainerOptions= trainerOptions
};
Action<RowWithKey,RowWithFloat>actionConvertKeyToFloat=(RowWithKeyrowWithKey,RowWithFloatrowWithFloat)=>
{
rowWithFloat.FloatLabel=rowWithKey.KeyLabel==0?float.NaN:rowWithKey.KeyLabel-1;
};
varsplit=ML.Data.TrainTestSplit(dataView,0.5);
vartrainData=split.TrainSet;
vartestData=split.TestSet;
varpipeline=ML.Transforms.Conversion.MapValueToKey("KeyLabel","Label")
.Append(ML.Transforms.CustomMapping(actionConvertKeyToFloat,"KeyLabel"))
.Append(ML.Transforms.FeaturizeByFastForestRegression(options))
.Append(ML.Transforms.Concatenate("CombinedFeatures","Trees","Leaves","Paths"))
.Append(ML.MulticlassClassification.Trainers.SdcaMaximumEntropy("KeyLabel","CombinedFeatures"));
varmodel=pipeline.Fit(trainData);
varprediction=model.Transform(testData);
varmetrics=ML.MulticlassClassification.Evaluate(prediction,labelColumnName:"KeyLabel");
Assert.True(metrics.MacroAccuracy>0.6);
Assert.True(metrics.MicroAccuracy>0.6);
}
class RowWithKey
{
[KeyType()]
publicuintKeyLabel{get;set;}
}
class RowWithFloat
{
publicfloatFloatLabel{get;set;}
}

Specifically, this is using a CustomMapping() to convert the Key to a float for use in the TreeFeat's FastForest regression. The current method in the unit test requires a user to know/list all of the values in their Label (and their type). The CustomMapping() style is easier for a user to replicate for their dataset.

We also added a TrainTestSplit() so we're not testing on the training set, and we removed the original features from the Concatenate() to ensure the TreeFeat's output features are useful.

@codemzs
codemzs requested review from artidoro and ganikJune 17, 2019 17:11
public TreeEnsembleModelParameters ModelParameters;
};

private TreeEnsembleModelParameters _modelParameters;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

TreeEnsembleModelParameters [](start = 16, length = 27)

Should this be readonly or something similar to make sure it is not altered?

@artidoro

artidoro commented Jun 20, 2019

Copy link
Copy Markdown
Contributor

Since you have already built all this infrastructure why are we not providing the featurizers for LightGbm trainers? I think they still use the same base class for the tree ensemble.
I guess this could come in another PR.

// The 0-1 encoding of leaves the input feature vector falls into.
public float[] Leaves { get; set; }
// The 0-1 encoding of paths the input feature vector reaches the leaves.
public float[] Paths { get; set; }

@artidoroartidoroJun 20, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit (same in other places if you are doing a revision of the PR):
// The 0-1 encoding of paths the input feature vector follows to reach the leaves.

/// and the i-th vector element is the prediction value predicted by the i-th tree.
/// If <see cref="TreesColumnName"/> is <see langword="null"/>, this output column may not be generated.
/// </summary>
public string TreesColumnName;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

TreesColumnName [](start = 26, length = 15)

Suggested renaming:

TreesColumnName -> TreeOutputsColumnName

I think it would be easier to understand, but this is not necessary.

@artidoroartidoro left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

:shipit:

@abgoswamabgoswam left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

:shipit:

@wschin
wschin merged commit 9d29111 into dotnet:masterJun 26, 2019
@wschin
wschin deleted the tree-feat branch June 26, 2019 23:15
Dmitry-A pushed a commit to Dmitry-A/machinelearning that referenced this pull request Jul 24, 2019
* Implement transformer
* Initial draft of porting tree-based featurization
* Internalize something
* Add Tweedie and Ranking cases
* Some small docs
* Customize output column names
* Fix save and load
* Optional output columns
* Fix a test and add some XML docs
* Add samples
* Add a sample
* API docs
* Fix one line
* Add MC test
* Extend a test further
* Address some comments
* Address some comments
* Address comments
* Comment
* Add cache points
* Update test/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs
Co-Authored-By: Justin Ormont <justinormont@users.noreply.github.com>
* Address comment
* Add Justin's test
* Reduce sample size
* Update sample output
@ghostghost locked as resolved and limited conversation to collaborators Mar 21, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TreeEnsembleFeaturizer is not a Transformer yet

6 participants

@wschin@artidoro@Ivanidzo4ka@justinormont@eerhardt@abgoswam
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Tree-based featurization - #3812

Merged
wschin merged 27 commits into
dotnet:masterfrom
wschin:tree-feat
Jun 26, 2019
Merged

Tree-based featurization#3812
wschin merged 27 commits into
dotnet:masterfrom
wschin:tree-feat

Conversation

@wschin

@wschinwschin commented Jun 3, 2019

Copy link
Copy Markdown
Contributor

Fix#2482. Generating features using tree structure has been a popular technique in data mining. This PR exposes this internal-only feature to the public.

Since I don't have enough time to handle multiple different assignments at the same time, please don't put nit comments and create new issues instead. Thanks a lot.

@wschinwschin self-assigned this Jun 3, 2019
return new FastForestBinaryTrainer(env, options);
}

public static PretrainedTreeFeaturizationEstimator PretrainTreeEnsembleFeaturizing(this TransformsCatalog catalog,

@eerhardteerhardtJun 3, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

XML Doc on all public classes and APIs. #Resolved

@wschinwschinJun 3, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

No problem. I am working on them. #Resolved

return new PretrainedTreeFeaturizationEstimator(env, options);
}

public static FastForestRegressionFeaturizationEstimator FastForestRegressionFeaturizing(this TransformsCatalog catalog,

@eerhardteerhardtJun 3, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This naming pattern reads a little funny. How about turning it into FeaturizeXXX? Like we have with FeaturizeText. #Resolved

@wschinwschinJun 3, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I can do FeaturizeBy.... #Resolved

@eerhardteerhardtJun 3, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That sounds better (to me at least). #Resolved

/// "Leaves" + <see cref="OutputColumnsSuffix"/>, and "Paths" + <see cref="OutputColumnsSuffix"/>. If <see cref="OutputColumnsSuffix"/>
/// is <see langword="null"/>, the output names would be "Trees", "Leaves", and "Paths".
/// </summary>
public string OutputColumnsSuffix;

@justinormontjustinormontJun 3, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We went away from magic strings in the TextTransform. Previously with tokens=+, we produced a new column named {OutputColName}_TokenizedText.

For the estimators API we have users directly enter the column name for the tokens. We may want to do the same for the Trees/Leaves/Paths of the TreeFeat.

Perhaps:
OutputColumnTreeName, OutputColumnLeavesName, OutputColumnPathsName.

#Resolved

@Ivanidzo4kaIvanidzo4kaJun 4, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

And don't add that column if they empty or equal to null.
That way you can actually configure which parts of tree structure do you want. #Resolved

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

As further background on the PR I was referencing...

Conversation about TextTransform:
via @rogancarr in #2957 PR

When using OutputTokens=true, FeaturizeText creates a new column called ${OutputColumnName}_TransformedText. This isn't really well documented anywhere, and it's odd behavior. I suggest that we make the tokenized text column name explicit in the API.

My suggestion would be the following:

  • Change OutputTokens = [bool] to OutputTokensColumn = [string], and a string.NullOrWhitespace(OutputTokensColumn) signifies that this column will not be created. #Resolved

@wschinwschinJun 5, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Now we can do optional output columns and custom output column names. Please see tests for examples (or wait for formal API samples). #Resolved

/// <param name="catalog">The context <see cref="TransformsCatalog"/> to create <see cref="FastTreeTweedieFeaturizationEstimator"/>.</param>
/// <param name="options">The options to configure <see cref="FastTreeTweedieFeaturizationEstimator"/>. See <see cref="FastTreeTweedieFeaturizationEstimator.Options"/> and
/// <see cref="TreeEnsembleFeaturizationEstimatorBase.CommonOptions"/> for available settings.</param>
public static FastTreeTweedieFeaturizationEstimator FeaturizeByFastTreeTweedie(this TransformsCatalog catalog,

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

May want to note in its name that FastTreeTweedie is regression: (the naming of the others list their task types)

Suggested change
publicstaticFastTreeTweedieFeaturizationEstimatorFeaturizeByFastTreeTweedie(thisTransformsCatalogcatalog,
publicstaticFastTreeTweedieRegressionFeaturizationEstimatorFeaturizeByFastTreeTweedieRegression(thisTransformsCatalogcatalog,
``` #ByDesign

@wschinwschinJun 5, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Yes if the model name doesn't tell the task. Given that Tweedie somehow implies a regression case, we don't have Regression appended to any of public Tweedie modules. This pattern can be seen in FastTreeTweedieTrainer and FastTreeTweedieModelParameters. #Resolved

TrainerOptions = trainerOptions
};

var pipeline = ML.Transforms.FeaturizeByFastTreeBinary(options).

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Creating (8) seperate ML.Transforms.FeaturizeBy{ FastTreeBinary, FastForestRegression, FastTreeRegression, FastTreeTweedie, ... } featurizers under ML.Transforms.* seems a bit large. Seems to clutter up the namespace.

In the future, this list should grow as I think we should have LightGBM variants too (and CatBoost if we take it in). The number of independent featurizers will be:
{ FastTree, FastTreeTweedie, FastForest, LightGBM, CatBoost } x { BinaryClassification, Multiclass, Regression, Ranking }`. (with some combinations missing)

Would it be more clean to have one ML.Transforms.TreeFeaturizer(), and put the specific instance type as a parameter? Would it be doable to have one return type? #ByDesign

@wschinwschinJun 5, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

There are two reasons that I don't like a single TreeFeaturizer.

  1. It goes toward an opposite direction of the C# API's design. Most of them are strongly typed to the underlying data structures. You can see we have a lot of FastTree... and LightGbm..., which is intended.
  2. It may requires user to specify TreeFeaturizer<TTrainer>(options) and the user manually needs to make sure the type of options is TTrainer.Options, which is easy to make mistakes. #Resolved

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You're right, the current pattern will likely have less mistakes by the user.

For the plethora of FastTree... and LightGBM..., those are namespaced under the task mlContext.Regression.Trainers.FastTree(), hence the duplication isn't visible.

TrainerOptions = trainerOptions
};

var pipeline = ML.Transforms.FeaturizeByFastForestRegression(options).

@justinormontjustinormontJun 5, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you add a test of ML.Transforms.FeaturizeByFastForestRegression() using FastForest's ShuffleLabels option? I believe this is the only way to use TreeFeat w/ multi-class classification.

@wschinwschinJun 6, 2019

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Let's make it in another PR. This PR has been too large.. #Resolved

@justinormontjustinormontJun 6, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems the UnitTests should be in the main PR. Specifically, I think we should ensure the multi-class case can work. #Resolved

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for adding.
#Resolved

@wschinwschin changed the title [WIP] Tree-based featurizationTree-based featurizationJun 6, 2019
@wschin
wschin requested review from Ivanidzo4ka, eerhardt and justinormont and removed request for Ivanidzo4ka and justinormontJune 6, 2019 17:58
Comment threadsrc/Microsoft.ML.FastTree/TreeEnsembleFeaturizer.cs
@wschin
wschin requested a review from eerhardtJune 6, 2019 22:10
Comment threadtest/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs Outdated
Comment threadtest/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs Outdated
}

[Fact]
public void TreeEnsembleFeaturizingPipelineMulticlass()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@daholste and I were able to map the Key to a float using a CustomMapping().

I'd recommend the multiclass unit test be:

Suggested change
publicvoidTreeEnsembleFeaturizingPipelineMulticlass()
[Fact]
publicvoidTreeEnsembleFeaturizingPipelineMulticlass()
{
intdataPointCount=1000;
vardata=SamplesUtils.DatasetUtils.GenerateRandomMulticlassClassificationExamples(dataPointCount).ToList();
vardataView=ML.Data.LoadFromEnumerable(data);
dataView=ML.Data.Cache(dataView);
vartrainerOptions=newFastForestRegressionTrainer.Options
{
NumberOfThreads=1,
NumberOfTrees=10,
NumberOfLeaves=4,
MinimumExampleCountPerLeaf=10,
FeatureColumnName="Features",
LabelColumnName="FloatLabel",
ShuffleLabels=true
};
varoptions=newFastForestRegressionFeaturizationEstimator.Options()
{
InputColumnName="Features",
TreesColumnName="Trees",
LeavesColumnName="Leaves",
PathsColumnName="Paths",
TrainerOptions= trainerOptions
};
Action<RowWithKey,RowWithFloat>actionConvertKeyToFloat=(RowWithKeyrowWithKey,RowWithFloatrowWithFloat)=>
{
rowWithFloat.FloatLabel=rowWithKey.KeyLabel==0?float.NaN:rowWithKey.KeyLabel-1;
};
varsplit=ML.Data.TrainTestSplit(dataView,0.5);
vartrainData=split.TrainSet;
vartestData=split.TestSet;
varpipeline=ML.Transforms.Conversion.MapValueToKey("KeyLabel","Label")
.Append(ML.Transforms.CustomMapping(actionConvertKeyToFloat,"KeyLabel"))
.Append(ML.Transforms.FeaturizeByFastForestRegression(options))
.Append(ML.Transforms.Concatenate("CombinedFeatures","Trees","Leaves","Paths"))
.Append(ML.MulticlassClassification.Trainers.SdcaMaximumEntropy("KeyLabel","CombinedFeatures"));
varmodel=pipeline.Fit(trainData);
varprediction=model.Transform(testData);
varmetrics=ML.MulticlassClassification.Evaluate(prediction,labelColumnName:"KeyLabel");
Assert.True(metrics.MacroAccuracy>0.6);
Assert.True(metrics.MicroAccuracy>0.6);
}
class RowWithKey
{
[KeyType()]
publicuintKeyLabel{get;set;}
}
class RowWithFloat
{
publicfloatFloatLabel{get;set;}
}

Specifically, this is using a CustomMapping() to convert the Key to a float for use in the TreeFeat's FastForest regression. The current method in the unit test requires a user to know/list all of the values in their Label (and their type). The CustomMapping() style is easier for a user to replicate for their dataset.

We also added a TrainTestSplit() so we're not testing on the training set, and we removed the original features from the Concatenate() to ensure the TreeFeat's output features are useful.

@codemzs
codemzs requested review from artidoro and ganikJune 17, 2019 17:11
public TreeEnsembleModelParameters ModelParameters;
};

private TreeEnsembleModelParameters _modelParameters;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

TreeEnsembleModelParameters [](start = 16, length = 27)

Should this be readonly or something similar to make sure it is not altered?

@artidoro

artidoro commented Jun 20, 2019

Copy link
Copy Markdown
Contributor

Since you have already built all this infrastructure why are we not providing the featurizers for LightGbm trainers? I think they still use the same base class for the tree ensemble.
I guess this could come in another PR.

// The 0-1 encoding of leaves the input feature vector falls into.
public float[] Leaves { get; set; }
// The 0-1 encoding of paths the input feature vector reaches the leaves.
public float[] Paths { get; set; }

@artidoroartidoroJun 20, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit (same in other places if you are doing a revision of the PR):
// The 0-1 encoding of paths the input feature vector follows to reach the leaves.

/// and the i-th vector element is the prediction value predicted by the i-th tree.
/// If <see cref="TreesColumnName"/> is <see langword="null"/>, this output column may not be generated.
/// </summary>
public string TreesColumnName;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

TreesColumnName [](start = 26, length = 15)

Suggested renaming:

TreesColumnName -> TreeOutputsColumnName

I think it would be easier to understand, but this is not necessary.

@artidoroartidoro left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

:shipit:

@abgoswamabgoswam left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

:shipit:

@wschin
wschin merged commit 9d29111 into dotnet:masterJun 26, 2019
@wschin
wschin deleted the tree-feat branch June 26, 2019 23:15
Dmitry-A pushed a commit to Dmitry-A/machinelearning that referenced this pull request Jul 24, 2019
* Implement transformer
* Initial draft of porting tree-based featurization
* Internalize something
* Add Tweedie and Ranking cases
* Some small docs
* Customize output column names
* Fix save and load
* Optional output columns
* Fix a test and add some XML docs
* Add samples
* Add a sample
* API docs
* Fix one line
* Add MC test
* Extend a test further
* Address some comments
* Address some comments
* Address comments
* Comment
* Add cache points
* Update test/Microsoft.ML.Tests/TrainerEstimators/TreeEnsembleFeaturizerTest.cs
Co-Authored-By: Justin Ormont <justinormont@users.noreply.github.com>
* Address comment
* Add Justin's test
* Reduce sample size
* Update sample output
@ghostghost locked as resolved and limited conversation to collaborators Mar 21, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TreeEnsembleFeaturizer is not a Transformer yet

6 participants

@wschin@artidoro@Ivanidzo4ka@justinormont@eerhardt@abgoswam