Skip to content

OneVersusAllModelParameters Strongly Typed - #4013

Closed
michaelgsharp wants to merge 19 commits into
dotnet:masterfrom
michaelgsharp:OvaTyping
Closed

michaelgsharp wants to merge 19 commits into
dotnet:masterfrom
michaelgsharp:OvaTyping

Conversation

@michaelgsharp

Copy link
Copy Markdown
Contributor

Fixes #2467

We used to remove all Type information when we constructed our OneVersusAllModelParameters. This prevented the users from access the inner model without run-time casting.

This PR makes the OneVersusAllModelParameters strongly typed, and adds a strongly typed version of the OneVersusAllTrainer as it uses the OneVersusAllModelParameters. This change is no longer a breaking change to the current public api.

/// </summary>
[BestFriend]
internal enum PredictionKind
public enum PredictionKind

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.

These types aren't intended to be public. Can you keep them internal?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately not if we want the OneVersusAllModelParameters to be strongly typed and keep the backwards api compatibility. Since the original type was of IPredictorProducing<float>, once I type it that way then IPredictorProducing<T>, IPredictor, and PredictionKind have to all become public as well. The only other thought I had was that maybe I could change the type of the backwards compatible OneVersusAllModelParameters to be of type object instead of IPredictorProducing<T>. This will still keep the backwards API compatibility and would allow me to make those classes internal again.

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.

Another option would be to not have the base class be generic. Have class OneVersusAllModelParametersBase and class OneVersusAllModelParameters : OneVersusAllModelParametersBase and class OneVersusAllModelParameters<T> : OneVersusAllModelParametersBase.

These IPredictor types are not meant to be public. We explicitly made them internal in 1.0 because they shouldn't be exposed to the user. So we will have to find another way here.

@michaelgsharp michaelgsharp Jul 18, 2019 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I thought about that approach initially, but there are enough Strongly typed parameters in there I didn't think it would work. Let me test it and see what I can do there. If not, we can always change the type to object since that will work and not change anything about the api.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Alright, I have made the change following your suggestions, so now the interfaces are back to being internal. Thanks for your feedback!

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.

PredictionKind should remain internal.

/// <summary>
/// Class that holds the static create methods for the <see cref="OneVersusAllModelParameters"/> classes.
/// </summary>
public class OneVersusAllModelParametersBuilder {

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 class doesn't need to be public. It doesn't contain any public members.

Also, it can be marked as a static class.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good points. I will change those.

/// This cannot be used to turn a non calibrated binary classification estimator into its calibrated version. If that is required, use <see cref="OneVersusAllTrainerTyped{TSubPredictor, TCalibrator}"/> instead.
/// </summary>
/// <typeparam name="T"></typeparam>
public sealed class OneVersusAllTrainerTyped<T> : OneVersusAllTrainerBase<OneVersusAllModelParametersBase<T>> where T : class

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.

I'm not sure having Typed at the end of this class name is the best. Why can't it just be OneVersusAllTrainer<T>?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The problem is that the only difference between the original method and this one are the optional parameters. So if people are only specifying the required parameters, which is the most common case in our code base, then C# doesn't know which method to call. Isn't that a binary breaking change? So I chose to make this method have a different name. Do you have any other ideas on how to resolve it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same question about the other method I added in. I called it OneVersusAllUnCalibratedToCalibratedTyped, but since it has 2 type parameters that have to be manually specified, its name can be anything and it wont have conflicts. I just named it this to be very clear on what it does, though I am happy to change it as well if you have thoughts on this too.

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 isn't a method, this is a class name. The class name and the method don't need to match 1-1.

If we need to suffix the method name with Typed or similar, that is fine. But the class doesn't need to have the suffix on it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I see. I was thinking of the Trainers Catalog when I responded. I have fixed the class names and will push those changes. Thanks!

/// [!code-csharp[OneVersusAll](~/../docs/samples/docs/samples/Microsoft.ML.Samples/Dynamic/Trainers/MulticlassClassification/OneVersusAll.cs)]
/// ]]></format>
/// </example>
public static OneVersusAllTrainer<TModel> OneVersusAllTyped<TModel>(this MulticlassClassificationCatalog.MulticlassClassificationTrainers catalog,

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.

Have you tested the new OneVersusAllTyped to see that you can match the scores of the existing one?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yep! I added and modified the tests in the OvaTest.cs to show that the scores match exactly.

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.

The test which directly compares the score of the non-typed to the newly created typed version, is the perfect unit test. Thanks.


Assert.Equal(metrics.MicroAccuracy, metricsTyped.MicroAccuracy);
}
//.Append(ML.MulticlassClassification.Trainers.OneVersusAllUnCalibratedToCalibratedTyped<LinearBinaryModelParameters, PlattCalibrator>(sdcaTrainer))

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.

Extraneous comment.

/// the binary classification estimator specified by <paramref name="binaryEstimator"/>.This method works with binary classifiers that
/// are not calibrated and need to be calibrated before use. Due to the type of estimator changing (from uncalibrated to calibrated), you must manually
/// specify both the type of the model and the type of the calibrator. If your classifier is already calibrated or it does not need to be, use the
/// <see cref="OneVersusAllTyped{TModel}"/> method instead.

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 explicitly state:
If you want to retrieve the model parameters, you will want to use this strongly typed OneVersusAllTrainer.

And perhaps a note on the non-strongly typed which says similar.

{
Host.Check(predictor is IParameterMixer<float>, "Predictor does not implement " + nameof(IParameterMixer<float>));
Host.Check(calibrator is IParameterMixer, "Calibrator does not implement " + nameof(IParameterMixer));
Host.Assert(predictor is IPredictorWithFeatureWeights<float>);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Assert [](start = 17, length = 6)

Can this be a check instead of assert?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

On second thought, why does this class need to have a generic type?


In reply to: 310376340 [](ancestors = 310376340)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This class needs to be generic to get the typed predictor and calibrator out of it. Otherwise OVA would just return this and it wouldn't be useful at all as far as getting the model goes. And I didn't do anything with the assert, so I am not sure why they did it that way in the beginning.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This specific class is internal anyway, so if OVA uses it users will not be able to get the typed calibrator anyway. But I don't think OVA uses this class, does it?


In reply to: 310687802 [](ancestors = 310687802)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

So when OVA is returning the sub model parameters, if the model is calibrated it will return an instance of the CalibratedModelParametersBase. This class is public, and still allows access to the model parameters and the calibrator used for the training. Any changes made in this file were to facilitate this CalibratedModelParametersBase and returning the correct type, while still keeping the derived classes abstract.

@codemzs

codemzs commented Oct 3, 2019

Copy link
Copy Markdown
Member

@michaelgsharp Closing this because this is not actively worked on.

@codemzs codemzs closed this Oct 3, 2019
@michaelgsharp
michaelgsharp deleted the OvaTyping branch December 3, 2019 22:38
@ghost ghost locked as resolved and limited conversation to collaborators Mar 21, 2022
Sign up for free to 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.

OvaModelParameters is not strongly-typed

5 participants