Use PFI with Binary Prediction Transformer and CalibratedModelParametersBase loaded from disk #4292

Description

@antoniovs1029

In my last accepted pull request (#4262 ) I addressed issue #3976 and was able to provide working samples and tests for using PFI with models loaded from disk except for the case of Binary Prediction Transformer. Here I open this issue about that specific problem.

Problem

In the sample using PFI with binary classification the last transformer of the model (i.e. the linearPredictor) is of type BinaryPredictionTransformer<CalibratedModelParametersBase<LinearBinaryModelParameters, PlattCalibrator>>.

Problem is that when saving and then loading that model from disk, a null reference is returned when trying to access the last transformer by casting it to the original type.

// linearPredictor is null:
var linearPredictor = (loadedmodel as TransformerChain<ITransformer>).LastTransformer as BinaryPredictionTransformer<CalibratedModelParametersBase<LinearBinaryModelParameters, PlattCalibrator>>; 

Having a null linearPredictor makes it unusable with PFI.

In version 1.3 of ML.Net the last transformer of the loaded model would actually be of type BinaryPredictionTransformer<IPredictorProducing<float>>

With the changes I made in my last PR (which will be available in version 1.4.0 preview 2) the loaded model's last transformer would be of type BinaryPredictionTransformer<ParameterMixingCalibratedModelParameters<IPredictorProducing<float>, ICalibrator>> which is a step forward in solving the problem, but is not yet enough.

As stated, in both cases, a cast to the original type would return null. In general, it would be expected that the user tries to make that cast in order to use PFI, failing to accomplish it.

This problem would be solved if the loaded model actually had a lastTransformer of the original type, or something castable to it.

Workaround

Based on this comment made by @yaeldekel I've just made this working sample of using PFI with a binary prediction transformer loaded from disk. It is pretty much the same as the original sample, only that it works with a model loaded from disk.

The key of the workaround is that the user should cast the lastTransformer not into a binary prediction transformer but rather into a ISingleFeaturePredictionTransformer<object>, and then do a series of casts to get whatever other object s/he may want to get from inside the lastTransformer.

In the sample I've just provided it works pretty much in this way:

var linearPredictor = (loadedmodel as TransformerChain<ITransformer>).LastTransformer as ISingleFeaturePredictionTransformer<object>;
var predictorModel = linearPredictor.Model as CalibratedModelParametersBase;
var predictorSubModel = predictorModel.SubModel as LinearBinaryModelParameters;

Notice that this workaround worked even in ML.Net 1.3, and also works with the changes that I introduced in 1.4.0 preview 2.

Notice that a similar workaround might help a user that tries to use PFI with any kind of prediction transformer loaded from disk. This would come useful if the user, for whatever reason, can not extract the linearPredictor by casting to the same type used in the original model.

Cause of the Problem

There are 3 main points that are related to the cause of this problem, all of which pertain the Calibrator.cs file and aren't related to the binary prediction transformer itself:

  1. Unexpectedly, when loading a ParameterMixingCalibratedModelParameters<> its Create method isn't called. I discovered this while debugging, and what actually happens is that, during loading, inside the CreateInstanceCore method, it first looks for a constructor, and so it calls the constructor of ParameterMixingCalibratedModelParameters<> instead of the Create method.
  2. Currently, when loading a ParameterMixingCalibratedModelParameters<> model, a ParameterMixingCalibratedModelParameters<IPredictorProducing<float>, ICalibrator> is always loaded, no matter what the actual submodel and calibrator are. This doesn't change by fixing point 1). This point is similar to the original problem found on the prediction transformers, which I fixed in my last pull request; using a similar approach in this case would fix this point... that is, loading first the submodel and calibrator to then create a generic type at runtime with the correct parameter types.
  3. When fiting the model (i.e. before even saving it or loading it) the SdcaLogisticRegressionBinaryTrainer creates a predictor of type ParameterMixingCalibratedModelParameters<LinearBinaryModelParameters, PlattCalibrator> (which I will now refer to as "PMCMP") but returns it as a CalibratedModelParametersBase<LinearBinaryModelParameters, PlattCalibrator> (let's call it "CMPB") this then is what makes the last transformer of the model to be a BinaryPredictionTransformer<CMPB> whereas the internal model of the last transformer is actually a PMCMP. When saving it to disk, it's saved as a PMCMP (i.e. it's saved using a LoaderSignature of "PMixCaliPredExec"), so when loading occurs, it calls the constructor of PMCMP but it doesn't cast it to a CMPB. This is different from the problem fixed in my last pull request; there, if a Regression prediction transformer was saved, then we expected to load a regression prediction transformer... whereas in here if a BPT is saved we actually want to load a PMCMP with the correct type parameters, but actually create a BPT where the CMPB should also have the correct type parameters.

Trying to solve the problem

So far I've been able to solve problems 1) and 2) described above, but after trying out different approaches I haven't been able to solve problem 3). To solve those problems I've changed different things in the Calibrator.cs file. My attempt to solve this problem can be found in PR #4306

With those changes (along with the ones of my last PR), the loaded model's last transformer becomes a BinaryPredictionTransformer<ParameterMixingCalibratedModelParameters<LinearBinaryModelParameters, PlattCalibrator>>. Notice that even here a cast to BPT<CMPB> would be null, so it doesn't solve the problem. Also notice that since PMCMP is an internal class the user wouldn't be able to cast the last transformer to BPT<PMCMP> either, since s/he wouldn't have access to that class.

Further problems

Here I've explained the specific case of loading a BPT<CMPB> with the specific problems that arise in CMPB and PMCMP classes because that is what is used in the sample of PFI with BPT, and in the tests of PFI with BPT. It could be possible that the problems here described are also present in other classes (for example in the other classes of Calibrator.cs) but they might not become a problem unless the user tries to access the last transformer of a model loaded from disk. In such a case the described workaround might help.

Activity

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    , '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

    Use PFI with Binary Prediction Transformer and CalibratedModelParametersBase loaded from disk #4292

    Description

    @antoniovs1029

    In my last accepted pull request (#4262 ) I addressed issue #3976 and was able to provide working samples and tests for using PFI with models loaded from disk except for the case of Binary Prediction Transformer. Here I open this issue about that specific problem.

    Problem

    In the sample using PFI with binary classification the last transformer of the model (i.e. the linearPredictor) is of type BinaryPredictionTransformer<CalibratedModelParametersBase<LinearBinaryModelParameters, PlattCalibrator>>.

    Problem is that when saving and then loading that model from disk, a null reference is returned when trying to access the last transformer by casting it to the original type.

    // linearPredictor is null:
    var linearPredictor = (loadedmodel as TransformerChain<ITransformer>).LastTransformer as BinaryPredictionTransformer<CalibratedModelParametersBase<LinearBinaryModelParameters, PlattCalibrator>>; 

    Having a null linearPredictor makes it unusable with PFI.

    In version 1.3 of ML.Net the last transformer of the loaded model would actually be of type BinaryPredictionTransformer<IPredictorProducing<float>>

    With the changes I made in my last PR (which will be available in version 1.4.0 preview 2) the loaded model's last transformer would be of type BinaryPredictionTransformer<ParameterMixingCalibratedModelParameters<IPredictorProducing<float>, ICalibrator>> which is a step forward in solving the problem, but is not yet enough.

    As stated, in both cases, a cast to the original type would return null. In general, it would be expected that the user tries to make that cast in order to use PFI, failing to accomplish it.

    This problem would be solved if the loaded model actually had a lastTransformer of the original type, or something castable to it.

    Workaround

    Based on this comment made by @yaeldekel I've just made this working sample of using PFI with a binary prediction transformer loaded from disk. It is pretty much the same as the original sample, only that it works with a model loaded from disk.

    The key of the workaround is that the user should cast the lastTransformer not into a binary prediction transformer but rather into a ISingleFeaturePredictionTransformer<object>, and then do a series of casts to get whatever other object s/he may want to get from inside the lastTransformer.

    In the sample I've just provided it works pretty much in this way:

    var linearPredictor = (loadedmodel as TransformerChain<ITransformer>).LastTransformer as ISingleFeaturePredictionTransformer<object>;
    var predictorModel = linearPredictor.Model as CalibratedModelParametersBase;
    var predictorSubModel = predictorModel.SubModel as LinearBinaryModelParameters;
    

    Notice that this workaround worked even in ML.Net 1.3, and also works with the changes that I introduced in 1.4.0 preview 2.

    Notice that a similar workaround might help a user that tries to use PFI with any kind of prediction transformer loaded from disk. This would come useful if the user, for whatever reason, can not extract the linearPredictor by casting to the same type used in the original model.

    Cause of the Problem

    There are 3 main points that are related to the cause of this problem, all of which pertain the Calibrator.cs file and aren't related to the binary prediction transformer itself:

    1. Unexpectedly, when loading a ParameterMixingCalibratedModelParameters<> its Create method isn't called. I discovered this while debugging, and what actually happens is that, during loading, inside the CreateInstanceCore method, it first looks for a constructor, and so it calls the constructor of ParameterMixingCalibratedModelParameters<> instead of the Create method.
    2. Currently, when loading a ParameterMixingCalibratedModelParameters<> model, a ParameterMixingCalibratedModelParameters<IPredictorProducing<float>, ICalibrator> is always loaded, no matter what the actual submodel and calibrator are. This doesn't change by fixing point 1). This point is similar to the original problem found on the prediction transformers, which I fixed in my last pull request; using a similar approach in this case would fix this point... that is, loading first the submodel and calibrator to then create a generic type at runtime with the correct parameter types.
    3. When fiting the model (i.e. before even saving it or loading it) the SdcaLogisticRegressionBinaryTrainer creates a predictor of type ParameterMixingCalibratedModelParameters<LinearBinaryModelParameters, PlattCalibrator> (which I will now refer to as "PMCMP") but returns it as a CalibratedModelParametersBase<LinearBinaryModelParameters, PlattCalibrator> (let's call it "CMPB") this then is what makes the last transformer of the model to be a BinaryPredictionTransformer<CMPB> whereas the internal model of the last transformer is actually a PMCMP. When saving it to disk, it's saved as a PMCMP (i.e. it's saved using a LoaderSignature of "PMixCaliPredExec"), so when loading occurs, it calls the constructor of PMCMP but it doesn't cast it to a CMPB. This is different from the problem fixed in my last pull request; there, if a Regression prediction transformer was saved, then we expected to load a regression prediction transformer... whereas in here if a BPT is saved we actually want to load a PMCMP with the correct type parameters, but actually create a BPT where the CMPB should also have the correct type parameters.

    Trying to solve the problem

    So far I've been able to solve problems 1) and 2) described above, but after trying out different approaches I haven't been able to solve problem 3). To solve those problems I've changed different things in the Calibrator.cs file. My attempt to solve this problem can be found in PR #4306

    With those changes (along with the ones of my last PR), the loaded model's last transformer becomes a BinaryPredictionTransformer<ParameterMixingCalibratedModelParameters<LinearBinaryModelParameters, PlattCalibrator>>. Notice that even here a cast to BPT<CMPB> would be null, so it doesn't solve the problem. Also notice that since PMCMP is an internal class the user wouldn't be able to cast the last transformer to BPT<PMCMP> either, since s/he wouldn't have access to that class.

    Further problems

    Here I've explained the specific case of loading a BPT<CMPB> with the specific problems that arise in CMPB and PMCMP classes because that is what is used in the sample of PFI with BPT, and in the tests of PFI with BPT. It could be possible that the problems here described are also present in other classes (for example in the other classes of Calibrator.cs) but they might not become a problem unless the user tries to access the last transformer of a model loaded from disk. In such a case the described workaround might help.

    Activity

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

    Metadata

    Metadata

    Assignees

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , '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

      Use PFI with Binary Prediction Transformer and CalibratedModelParametersBase loaded from disk #4292

      Description

      @antoniovs1029

      In my last accepted pull request (#4262 ) I addressed issue #3976 and was able to provide working samples and tests for using PFI with models loaded from disk except for the case of Binary Prediction Transformer. Here I open this issue about that specific problem.

      Problem

      In the sample using PFI with binary classification the last transformer of the model (i.e. the linearPredictor) is of type BinaryPredictionTransformer<CalibratedModelParametersBase<LinearBinaryModelParameters, PlattCalibrator>>.

      Problem is that when saving and then loading that model from disk, a null reference is returned when trying to access the last transformer by casting it to the original type.

      // linearPredictor is null:
      var linearPredictor = (loadedmodel as TransformerChain<ITransformer>).LastTransformer as BinaryPredictionTransformer<CalibratedModelParametersBase<LinearBinaryModelParameters, PlattCalibrator>>; 

      Having a null linearPredictor makes it unusable with PFI.

      In version 1.3 of ML.Net the last transformer of the loaded model would actually be of type BinaryPredictionTransformer<IPredictorProducing<float>>

      With the changes I made in my last PR (which will be available in version 1.4.0 preview 2) the loaded model's last transformer would be of type BinaryPredictionTransformer<ParameterMixingCalibratedModelParameters<IPredictorProducing<float>, ICalibrator>> which is a step forward in solving the problem, but is not yet enough.

      As stated, in both cases, a cast to the original type would return null. In general, it would be expected that the user tries to make that cast in order to use PFI, failing to accomplish it.

      This problem would be solved if the loaded model actually had a lastTransformer of the original type, or something castable to it.

      Workaround

      Based on this comment made by @yaeldekel I've just made this working sample of using PFI with a binary prediction transformer loaded from disk. It is pretty much the same as the original sample, only that it works with a model loaded from disk.

      The key of the workaround is that the user should cast the lastTransformer not into a binary prediction transformer but rather into a ISingleFeaturePredictionTransformer<object>, and then do a series of casts to get whatever other object s/he may want to get from inside the lastTransformer.

      In the sample I've just provided it works pretty much in this way:

      var linearPredictor = (loadedmodel as TransformerChain<ITransformer>).LastTransformer as ISingleFeaturePredictionTransformer<object>;
      var predictorModel = linearPredictor.Model as CalibratedModelParametersBase;
      var predictorSubModel = predictorModel.SubModel as LinearBinaryModelParameters;
      

      Notice that this workaround worked even in ML.Net 1.3, and also works with the changes that I introduced in 1.4.0 preview 2.

      Notice that a similar workaround might help a user that tries to use PFI with any kind of prediction transformer loaded from disk. This would come useful if the user, for whatever reason, can not extract the linearPredictor by casting to the same type used in the original model.

      Cause of the Problem

      There are 3 main points that are related to the cause of this problem, all of which pertain the Calibrator.cs file and aren't related to the binary prediction transformer itself:

      1. Unexpectedly, when loading a ParameterMixingCalibratedModelParameters<> its Create method isn't called. I discovered this while debugging, and what actually happens is that, during loading, inside the CreateInstanceCore method, it first looks for a constructor, and so it calls the constructor of ParameterMixingCalibratedModelParameters<> instead of the Create method.
      2. Currently, when loading a ParameterMixingCalibratedModelParameters<> model, a ParameterMixingCalibratedModelParameters<IPredictorProducing<float>, ICalibrator> is always loaded, no matter what the actual submodel and calibrator are. This doesn't change by fixing point 1). This point is similar to the original problem found on the prediction transformers, which I fixed in my last pull request; using a similar approach in this case would fix this point... that is, loading first the submodel and calibrator to then create a generic type at runtime with the correct parameter types.
      3. When fiting the model (i.e. before even saving it or loading it) the SdcaLogisticRegressionBinaryTrainer creates a predictor of type ParameterMixingCalibratedModelParameters<LinearBinaryModelParameters, PlattCalibrator> (which I will now refer to as "PMCMP") but returns it as a CalibratedModelParametersBase<LinearBinaryModelParameters, PlattCalibrator> (let's call it "CMPB") this then is what makes the last transformer of the model to be a BinaryPredictionTransformer<CMPB> whereas the internal model of the last transformer is actually a PMCMP. When saving it to disk, it's saved as a PMCMP (i.e. it's saved using a LoaderSignature of "PMixCaliPredExec"), so when loading occurs, it calls the constructor of PMCMP but it doesn't cast it to a CMPB. This is different from the problem fixed in my last pull request; there, if a Regression prediction transformer was saved, then we expected to load a regression prediction transformer... whereas in here if a BPT is saved we actually want to load a PMCMP with the correct type parameters, but actually create a BPT where the CMPB should also have the correct type parameters.

      Trying to solve the problem

      So far I've been able to solve problems 1) and 2) described above, but after trying out different approaches I haven't been able to solve problem 3). To solve those problems I've changed different things in the Calibrator.cs file. My attempt to solve this problem can be found in PR #4306

      With those changes (along with the ones of my last PR), the loaded model's last transformer becomes a BinaryPredictionTransformer<ParameterMixingCalibratedModelParameters<LinearBinaryModelParameters, PlattCalibrator>>. Notice that even here a cast to BPT<CMPB> would be null, so it doesn't solve the problem. Also notice that since PMCMP is an internal class the user wouldn't be able to cast the last transformer to BPT<PMCMP> either, since s/he wouldn't have access to that class.

      Further problems

      Here I've explained the specific case of loading a BPT<CMPB> with the specific problems that arise in CMPB and PMCMP classes because that is what is used in the sample of PFI with BPT, and in the tests of PFI with BPT. It could be possible that the problems here described are also present in other classes (for example in the other classes of Calibrator.cs) but they might not become a problem unless the user tries to access the last transformer of a model loaded from disk. In such a case the described workaround might help.

      Activity

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

      Metadata

      Metadata

      Assignees

      Labels

      No labels
      No labels

      Type

      No type

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions

        , '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

        Use PFI with Binary Prediction Transformer and CalibratedModelParametersBase loaded from disk #4292

        Description

        @antoniovs1029

        In my last accepted pull request (#4262 ) I addressed issue #3976 and was able to provide working samples and tests for using PFI with models loaded from disk except for the case of Binary Prediction Transformer. Here I open this issue about that specific problem.

        Problem

        In the sample using PFI with binary classification the last transformer of the model (i.e. the linearPredictor) is of type BinaryPredictionTransformer<CalibratedModelParametersBase<LinearBinaryModelParameters, PlattCalibrator>>.

        Problem is that when saving and then loading that model from disk, a null reference is returned when trying to access the last transformer by casting it to the original type.

        // linearPredictor is null:
        var linearPredictor = (loadedmodel as TransformerChain<ITransformer>).LastTransformer as BinaryPredictionTransformer<CalibratedModelParametersBase<LinearBinaryModelParameters, PlattCalibrator>>; 

        Having a null linearPredictor makes it unusable with PFI.

        In version 1.3 of ML.Net the last transformer of the loaded model would actually be of type BinaryPredictionTransformer<IPredictorProducing<float>>

        With the changes I made in my last PR (which will be available in version 1.4.0 preview 2) the loaded model's last transformer would be of type BinaryPredictionTransformer<ParameterMixingCalibratedModelParameters<IPredictorProducing<float>, ICalibrator>> which is a step forward in solving the problem, but is not yet enough.

        As stated, in both cases, a cast to the original type would return null. In general, it would be expected that the user tries to make that cast in order to use PFI, failing to accomplish it.

        This problem would be solved if the loaded model actually had a lastTransformer of the original type, or something castable to it.

        Workaround

        Based on this comment made by @yaeldekel I've just made this working sample of using PFI with a binary prediction transformer loaded from disk. It is pretty much the same as the original sample, only that it works with a model loaded from disk.

        The key of the workaround is that the user should cast the lastTransformer not into a binary prediction transformer but rather into a ISingleFeaturePredictionTransformer<object>, and then do a series of casts to get whatever other object s/he may want to get from inside the lastTransformer.

        In the sample I've just provided it works pretty much in this way:

        var linearPredictor = (loadedmodel as TransformerChain<ITransformer>).LastTransformer as ISingleFeaturePredictionTransformer<object>;
        var predictorModel = linearPredictor.Model as CalibratedModelParametersBase;
        var predictorSubModel = predictorModel.SubModel as LinearBinaryModelParameters;
        

        Notice that this workaround worked even in ML.Net 1.3, and also works with the changes that I introduced in 1.4.0 preview 2.

        Notice that a similar workaround might help a user that tries to use PFI with any kind of prediction transformer loaded from disk. This would come useful if the user, for whatever reason, can not extract the linearPredictor by casting to the same type used in the original model.

        Cause of the Problem

        There are 3 main points that are related to the cause of this problem, all of which pertain the Calibrator.cs file and aren't related to the binary prediction transformer itself:

        1. Unexpectedly, when loading a ParameterMixingCalibratedModelParameters<> its Create method isn't called. I discovered this while debugging, and what actually happens is that, during loading, inside the CreateInstanceCore method, it first looks for a constructor, and so it calls the constructor of ParameterMixingCalibratedModelParameters<> instead of the Create method.
        2. Currently, when loading a ParameterMixingCalibratedModelParameters<> model, a ParameterMixingCalibratedModelParameters<IPredictorProducing<float>, ICalibrator> is always loaded, no matter what the actual submodel and calibrator are. This doesn't change by fixing point 1). This point is similar to the original problem found on the prediction transformers, which I fixed in my last pull request; using a similar approach in this case would fix this point... that is, loading first the submodel and calibrator to then create a generic type at runtime with the correct parameter types.
        3. When fiting the model (i.e. before even saving it or loading it) the SdcaLogisticRegressionBinaryTrainer creates a predictor of type ParameterMixingCalibratedModelParameters<LinearBinaryModelParameters, PlattCalibrator> (which I will now refer to as "PMCMP") but returns it as a CalibratedModelParametersBase<LinearBinaryModelParameters, PlattCalibrator> (let's call it "CMPB") this then is what makes the last transformer of the model to be a BinaryPredictionTransformer<CMPB> whereas the internal model of the last transformer is actually a PMCMP. When saving it to disk, it's saved as a PMCMP (i.e. it's saved using a LoaderSignature of "PMixCaliPredExec"), so when loading occurs, it calls the constructor of PMCMP but it doesn't cast it to a CMPB. This is different from the problem fixed in my last pull request; there, if a Regression prediction transformer was saved, then we expected to load a regression prediction transformer... whereas in here if a BPT is saved we actually want to load a PMCMP with the correct type parameters, but actually create a BPT where the CMPB should also have the correct type parameters.

        Trying to solve the problem

        So far I've been able to solve problems 1) and 2) described above, but after trying out different approaches I haven't been able to solve problem 3). To solve those problems I've changed different things in the Calibrator.cs file. My attempt to solve this problem can be found in PR #4306

        With those changes (along with the ones of my last PR), the loaded model's last transformer becomes a BinaryPredictionTransformer<ParameterMixingCalibratedModelParameters<LinearBinaryModelParameters, PlattCalibrator>>. Notice that even here a cast to BPT<CMPB> would be null, so it doesn't solve the problem. Also notice that since PMCMP is an internal class the user wouldn't be able to cast the last transformer to BPT<PMCMP> either, since s/he wouldn't have access to that class.

        Further problems

        Here I've explained the specific case of loading a BPT<CMPB> with the specific problems that arise in CMPB and PMCMP classes because that is what is used in the sample of PFI with BPT, and in the tests of PFI with BPT. It could be possible that the problems here described are also present in other classes (for example in the other classes of Calibrator.cs) but they might not become a problem unless the user tries to access the last transformer of a model loaded from disk. In such a case the described workaround might help.

        Activity

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

        Metadata

        Metadata

        Assignees

        Labels

        No labels
        No labels

        Type

        No type

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , '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

          Use PFI with Binary Prediction Transformer and CalibratedModelParametersBase loaded from disk #4292

          Description

          @antoniovs1029

          In my last accepted pull request (#4262 ) I addressed issue #3976 and was able to provide working samples and tests for using PFI with models loaded from disk except for the case of Binary Prediction Transformer. Here I open this issue about that specific problem.

          Problem

          In the sample using PFI with binary classification the last transformer of the model (i.e. the linearPredictor) is of type BinaryPredictionTransformer<CalibratedModelParametersBase<LinearBinaryModelParameters, PlattCalibrator>>.

          Problem is that when saving and then loading that model from disk, a null reference is returned when trying to access the last transformer by casting it to the original type.

          // linearPredictor is null:
          var linearPredictor = (loadedmodel as TransformerChain<ITransformer>).LastTransformer as BinaryPredictionTransformer<CalibratedModelParametersBase<LinearBinaryModelParameters, PlattCalibrator>>; 

          Having a null linearPredictor makes it unusable with PFI.

          In version 1.3 of ML.Net the last transformer of the loaded model would actually be of type BinaryPredictionTransformer<IPredictorProducing<float>>

          With the changes I made in my last PR (which will be available in version 1.4.0 preview 2) the loaded model's last transformer would be of type BinaryPredictionTransformer<ParameterMixingCalibratedModelParameters<IPredictorProducing<float>, ICalibrator>> which is a step forward in solving the problem, but is not yet enough.

          As stated, in both cases, a cast to the original type would return null. In general, it would be expected that the user tries to make that cast in order to use PFI, failing to accomplish it.

          This problem would be solved if the loaded model actually had a lastTransformer of the original type, or something castable to it.

          Workaround

          Based on this comment made by @yaeldekel I've just made this working sample of using PFI with a binary prediction transformer loaded from disk. It is pretty much the same as the original sample, only that it works with a model loaded from disk.

          The key of the workaround is that the user should cast the lastTransformer not into a binary prediction transformer but rather into a ISingleFeaturePredictionTransformer<object>, and then do a series of casts to get whatever other object s/he may want to get from inside the lastTransformer.

          In the sample I've just provided it works pretty much in this way:

          var linearPredictor = (loadedmodel as TransformerChain<ITransformer>).LastTransformer as ISingleFeaturePredictionTransformer<object>;
          var predictorModel = linearPredictor.Model as CalibratedModelParametersBase;
          var predictorSubModel = predictorModel.SubModel as LinearBinaryModelParameters;
          

          Notice that this workaround worked even in ML.Net 1.3, and also works with the changes that I introduced in 1.4.0 preview 2.

          Notice that a similar workaround might help a user that tries to use PFI with any kind of prediction transformer loaded from disk. This would come useful if the user, for whatever reason, can not extract the linearPredictor by casting to the same type used in the original model.

          Cause of the Problem

          There are 3 main points that are related to the cause of this problem, all of which pertain the Calibrator.cs file and aren't related to the binary prediction transformer itself:

          1. Unexpectedly, when loading a ParameterMixingCalibratedModelParameters<> its Create method isn't called. I discovered this while debugging, and what actually happens is that, during loading, inside the CreateInstanceCore method, it first looks for a constructor, and so it calls the constructor of ParameterMixingCalibratedModelParameters<> instead of the Create method.
          2. Currently, when loading a ParameterMixingCalibratedModelParameters<> model, a ParameterMixingCalibratedModelParameters<IPredictorProducing<float>, ICalibrator> is always loaded, no matter what the actual submodel and calibrator are. This doesn't change by fixing point 1). This point is similar to the original problem found on the prediction transformers, which I fixed in my last pull request; using a similar approach in this case would fix this point... that is, loading first the submodel and calibrator to then create a generic type at runtime with the correct parameter types.
          3. When fiting the model (i.e. before even saving it or loading it) the SdcaLogisticRegressionBinaryTrainer creates a predictor of type ParameterMixingCalibratedModelParameters<LinearBinaryModelParameters, PlattCalibrator> (which I will now refer to as "PMCMP") but returns it as a CalibratedModelParametersBase<LinearBinaryModelParameters, PlattCalibrator> (let's call it "CMPB") this then is what makes the last transformer of the model to be a BinaryPredictionTransformer<CMPB> whereas the internal model of the last transformer is actually a PMCMP. When saving it to disk, it's saved as a PMCMP (i.e. it's saved using a LoaderSignature of "PMixCaliPredExec"), so when loading occurs, it calls the constructor of PMCMP but it doesn't cast it to a CMPB. This is different from the problem fixed in my last pull request; there, if a Regression prediction transformer was saved, then we expected to load a regression prediction transformer... whereas in here if a BPT is saved we actually want to load a PMCMP with the correct type parameters, but actually create a BPT where the CMPB should also have the correct type parameters.

          Trying to solve the problem

          So far I've been able to solve problems 1) and 2) described above, but after trying out different approaches I haven't been able to solve problem 3). To solve those problems I've changed different things in the Calibrator.cs file. My attempt to solve this problem can be found in PR #4306

          With those changes (along with the ones of my last PR), the loaded model's last transformer becomes a BinaryPredictionTransformer<ParameterMixingCalibratedModelParameters<LinearBinaryModelParameters, PlattCalibrator>>. Notice that even here a cast to BPT<CMPB> would be null, so it doesn't solve the problem. Also notice that since PMCMP is an internal class the user wouldn't be able to cast the last transformer to BPT<PMCMP> either, since s/he wouldn't have access to that class.

          Further problems

          Here I've explained the specific case of loading a BPT<CMPB> with the specific problems that arise in CMPB and PMCMP classes because that is what is used in the sample of PFI with BPT, and in the tests of PFI with BPT. It could be possible that the problems here described are also present in other classes (for example in the other classes of Calibrator.cs) but they might not become a problem unless the user tries to access the last transformer of a model loaded from disk. In such a case the described workaround might help.

          Activity

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

          Metadata

          Metadata

          Assignees

          Labels

          No labels
          No labels

          Type

          No type

          Projects

          No projects

            Milestone

            No milestone

            Relationships

            None yet

            Development

            No branches or pull requests

            Issue actions

            , '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

            Use PFI with Binary Prediction Transformer and CalibratedModelParametersBase loaded from disk #4292

            Description

            @antoniovs1029

            In my last accepted pull request (#4262 ) I addressed issue #3976 and was able to provide working samples and tests for using PFI with models loaded from disk except for the case of Binary Prediction Transformer. Here I open this issue about that specific problem.

            Problem

            In the sample using PFI with binary classification the last transformer of the model (i.e. the linearPredictor) is of type BinaryPredictionTransformer<CalibratedModelParametersBase<LinearBinaryModelParameters, PlattCalibrator>>.

            Problem is that when saving and then loading that model from disk, a null reference is returned when trying to access the last transformer by casting it to the original type.

            // linearPredictor is null:
            var linearPredictor = (loadedmodel as TransformerChain<ITransformer>).LastTransformer as BinaryPredictionTransformer<CalibratedModelParametersBase<LinearBinaryModelParameters, PlattCalibrator>>; 

            Having a null linearPredictor makes it unusable with PFI.

            In version 1.3 of ML.Net the last transformer of the loaded model would actually be of type BinaryPredictionTransformer<IPredictorProducing<float>>

            With the changes I made in my last PR (which will be available in version 1.4.0 preview 2) the loaded model's last transformer would be of type BinaryPredictionTransformer<ParameterMixingCalibratedModelParameters<IPredictorProducing<float>, ICalibrator>> which is a step forward in solving the problem, but is not yet enough.

            As stated, in both cases, a cast to the original type would return null. In general, it would be expected that the user tries to make that cast in order to use PFI, failing to accomplish it.

            This problem would be solved if the loaded model actually had a lastTransformer of the original type, or something castable to it.

            Workaround

            Based on this comment made by @yaeldekel I've just made this working sample of using PFI with a binary prediction transformer loaded from disk. It is pretty much the same as the original sample, only that it works with a model loaded from disk.

            The key of the workaround is that the user should cast the lastTransformer not into a binary prediction transformer but rather into a ISingleFeaturePredictionTransformer<object>, and then do a series of casts to get whatever other object s/he may want to get from inside the lastTransformer.

            In the sample I've just provided it works pretty much in this way:

            var linearPredictor = (loadedmodel as TransformerChain<ITransformer>).LastTransformer as ISingleFeaturePredictionTransformer<object>;
            var predictorModel = linearPredictor.Model as CalibratedModelParametersBase;
            var predictorSubModel = predictorModel.SubModel as LinearBinaryModelParameters;
            

            Notice that this workaround worked even in ML.Net 1.3, and also works with the changes that I introduced in 1.4.0 preview 2.

            Notice that a similar workaround might help a user that tries to use PFI with any kind of prediction transformer loaded from disk. This would come useful if the user, for whatever reason, can not extract the linearPredictor by casting to the same type used in the original model.

            Cause of the Problem

            There are 3 main points that are related to the cause of this problem, all of which pertain the Calibrator.cs file and aren't related to the binary prediction transformer itself:

            1. Unexpectedly, when loading a ParameterMixingCalibratedModelParameters<> its Create method isn't called. I discovered this while debugging, and what actually happens is that, during loading, inside the CreateInstanceCore method, it first looks for a constructor, and so it calls the constructor of ParameterMixingCalibratedModelParameters<> instead of the Create method.
            2. Currently, when loading a ParameterMixingCalibratedModelParameters<> model, a ParameterMixingCalibratedModelParameters<IPredictorProducing<float>, ICalibrator> is always loaded, no matter what the actual submodel and calibrator are. This doesn't change by fixing point 1). This point is similar to the original problem found on the prediction transformers, which I fixed in my last pull request; using a similar approach in this case would fix this point... that is, loading first the submodel and calibrator to then create a generic type at runtime with the correct parameter types.
            3. When fiting the model (i.e. before even saving it or loading it) the SdcaLogisticRegressionBinaryTrainer creates a predictor of type ParameterMixingCalibratedModelParameters<LinearBinaryModelParameters, PlattCalibrator> (which I will now refer to as "PMCMP") but returns it as a CalibratedModelParametersBase<LinearBinaryModelParameters, PlattCalibrator> (let's call it "CMPB") this then is what makes the last transformer of the model to be a BinaryPredictionTransformer<CMPB> whereas the internal model of the last transformer is actually a PMCMP. When saving it to disk, it's saved as a PMCMP (i.e. it's saved using a LoaderSignature of "PMixCaliPredExec"), so when loading occurs, it calls the constructor of PMCMP but it doesn't cast it to a CMPB. This is different from the problem fixed in my last pull request; there, if a Regression prediction transformer was saved, then we expected to load a regression prediction transformer... whereas in here if a BPT is saved we actually want to load a PMCMP with the correct type parameters, but actually create a BPT where the CMPB should also have the correct type parameters.

            Trying to solve the problem

            So far I've been able to solve problems 1) and 2) described above, but after trying out different approaches I haven't been able to solve problem 3). To solve those problems I've changed different things in the Calibrator.cs file. My attempt to solve this problem can be found in PR #4306

            With those changes (along with the ones of my last PR), the loaded model's last transformer becomes a BinaryPredictionTransformer<ParameterMixingCalibratedModelParameters<LinearBinaryModelParameters, PlattCalibrator>>. Notice that even here a cast to BPT<CMPB> would be null, so it doesn't solve the problem. Also notice that since PMCMP is an internal class the user wouldn't be able to cast the last transformer to BPT<PMCMP> either, since s/he wouldn't have access to that class.

            Further problems

            Here I've explained the specific case of loading a BPT<CMPB> with the specific problems that arise in CMPB and PMCMP classes because that is what is used in the sample of PFI with BPT, and in the tests of PFI with BPT. It could be possible that the problems here described are also present in other classes (for example in the other classes of Calibrator.cs) but they might not become a problem unless the user tries to access the last transformer of a model loaded from disk. In such a case the described workaround might help.

            Activity

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

            Metadata

            Metadata

            Assignees

            Labels

            No labels
            No labels

            Type

            No type

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , '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

              Use PFI with Binary Prediction Transformer and CalibratedModelParametersBase loaded from disk #4292

              Description

              @antoniovs1029

              In my last accepted pull request (#4262 ) I addressed issue #3976 and was able to provide working samples and tests for using PFI with models loaded from disk except for the case of Binary Prediction Transformer. Here I open this issue about that specific problem.

              Problem

              In the sample using PFI with binary classification the last transformer of the model (i.e. the linearPredictor) is of type BinaryPredictionTransformer<CalibratedModelParametersBase<LinearBinaryModelParameters, PlattCalibrator>>.

              Problem is that when saving and then loading that model from disk, a null reference is returned when trying to access the last transformer by casting it to the original type.

              // linearPredictor is null:
              var linearPredictor = (loadedmodel as TransformerChain<ITransformer>).LastTransformer as BinaryPredictionTransformer<CalibratedModelParametersBase<LinearBinaryModelParameters, PlattCalibrator>>; 

              Having a null linearPredictor makes it unusable with PFI.

              In version 1.3 of ML.Net the last transformer of the loaded model would actually be of type BinaryPredictionTransformer<IPredictorProducing<float>>

              With the changes I made in my last PR (which will be available in version 1.4.0 preview 2) the loaded model's last transformer would be of type BinaryPredictionTransformer<ParameterMixingCalibratedModelParameters<IPredictorProducing<float>, ICalibrator>> which is a step forward in solving the problem, but is not yet enough.

              As stated, in both cases, a cast to the original type would return null. In general, it would be expected that the user tries to make that cast in order to use PFI, failing to accomplish it.

              This problem would be solved if the loaded model actually had a lastTransformer of the original type, or something castable to it.

              Workaround

              Based on this comment made by @yaeldekel I've just made this working sample of using PFI with a binary prediction transformer loaded from disk. It is pretty much the same as the original sample, only that it works with a model loaded from disk.

              The key of the workaround is that the user should cast the lastTransformer not into a binary prediction transformer but rather into a ISingleFeaturePredictionTransformer<object>, and then do a series of casts to get whatever other object s/he may want to get from inside the lastTransformer.

              In the sample I've just provided it works pretty much in this way:

              var linearPredictor = (loadedmodel as TransformerChain<ITransformer>).LastTransformer as ISingleFeaturePredictionTransformer<object>;
              var predictorModel = linearPredictor.Model as CalibratedModelParametersBase;
              var predictorSubModel = predictorModel.SubModel as LinearBinaryModelParameters;
              

              Notice that this workaround worked even in ML.Net 1.3, and also works with the changes that I introduced in 1.4.0 preview 2.

              Notice that a similar workaround might help a user that tries to use PFI with any kind of prediction transformer loaded from disk. This would come useful if the user, for whatever reason, can not extract the linearPredictor by casting to the same type used in the original model.

              Cause of the Problem

              There are 3 main points that are related to the cause of this problem, all of which pertain the Calibrator.cs file and aren't related to the binary prediction transformer itself:

              1. Unexpectedly, when loading a ParameterMixingCalibratedModelParameters<> its Create method isn't called. I discovered this while debugging, and what actually happens is that, during loading, inside the CreateInstanceCore method, it first looks for a constructor, and so it calls the constructor of ParameterMixingCalibratedModelParameters<> instead of the Create method.
              2. Currently, when loading a ParameterMixingCalibratedModelParameters<> model, a ParameterMixingCalibratedModelParameters<IPredictorProducing<float>, ICalibrator> is always loaded, no matter what the actual submodel and calibrator are. This doesn't change by fixing point 1). This point is similar to the original problem found on the prediction transformers, which I fixed in my last pull request; using a similar approach in this case would fix this point... that is, loading first the submodel and calibrator to then create a generic type at runtime with the correct parameter types.
              3. When fiting the model (i.e. before even saving it or loading it) the SdcaLogisticRegressionBinaryTrainer creates a predictor of type ParameterMixingCalibratedModelParameters<LinearBinaryModelParameters, PlattCalibrator> (which I will now refer to as "PMCMP") but returns it as a CalibratedModelParametersBase<LinearBinaryModelParameters, PlattCalibrator> (let's call it "CMPB") this then is what makes the last transformer of the model to be a BinaryPredictionTransformer<CMPB> whereas the internal model of the last transformer is actually a PMCMP. When saving it to disk, it's saved as a PMCMP (i.e. it's saved using a LoaderSignature of "PMixCaliPredExec"), so when loading occurs, it calls the constructor of PMCMP but it doesn't cast it to a CMPB. This is different from the problem fixed in my last pull request; there, if a Regression prediction transformer was saved, then we expected to load a regression prediction transformer... whereas in here if a BPT is saved we actually want to load a PMCMP with the correct type parameters, but actually create a BPT where the CMPB should also have the correct type parameters.

              Trying to solve the problem

              So far I've been able to solve problems 1) and 2) described above, but after trying out different approaches I haven't been able to solve problem 3). To solve those problems I've changed different things in the Calibrator.cs file. My attempt to solve this problem can be found in PR #4306

              With those changes (along with the ones of my last PR), the loaded model's last transformer becomes a BinaryPredictionTransformer<ParameterMixingCalibratedModelParameters<LinearBinaryModelParameters, PlattCalibrator>>. Notice that even here a cast to BPT<CMPB> would be null, so it doesn't solve the problem. Also notice that since PMCMP is an internal class the user wouldn't be able to cast the last transformer to BPT<PMCMP> either, since s/he wouldn't have access to that class.

              Further problems

              Here I've explained the specific case of loading a BPT<CMPB> with the specific problems that arise in CMPB and PMCMP classes because that is what is used in the sample of PFI with BPT, and in the tests of PFI with BPT. It could be possible that the problems here described are also present in other classes (for example in the other classes of Calibrator.cs) but they might not become a problem unless the user tries to access the last transformer of a model loaded from disk. In such a case the described workaround might help.

              Activity

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

              Metadata

              Metadata

              Assignees

              Labels

              No labels
              No labels

              Type

              No type

              Projects

              No projects

                Milestone

                No milestone

                Relationships

                None yet

                Development

                No branches or pull requests

                Issue actions

                , '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

                Use PFI with Binary Prediction Transformer and CalibratedModelParametersBase loaded from disk #4292

                Description

                @antoniovs1029

                In my last accepted pull request (#4262 ) I addressed issue #3976 and was able to provide working samples and tests for using PFI with models loaded from disk except for the case of Binary Prediction Transformer. Here I open this issue about that specific problem.

                Problem

                In the sample using PFI with binary classification the last transformer of the model (i.e. the linearPredictor) is of type BinaryPredictionTransformer<CalibratedModelParametersBase<LinearBinaryModelParameters, PlattCalibrator>>.

                Problem is that when saving and then loading that model from disk, a null reference is returned when trying to access the last transformer by casting it to the original type.

                // linearPredictor is null:
                var linearPredictor = (loadedmodel as TransformerChain<ITransformer>).LastTransformer as BinaryPredictionTransformer<CalibratedModelParametersBase<LinearBinaryModelParameters, PlattCalibrator>>; 

                Having a null linearPredictor makes it unusable with PFI.

                In version 1.3 of ML.Net the last transformer of the loaded model would actually be of type BinaryPredictionTransformer<IPredictorProducing<float>>

                With the changes I made in my last PR (which will be available in version 1.4.0 preview 2) the loaded model's last transformer would be of type BinaryPredictionTransformer<ParameterMixingCalibratedModelParameters<IPredictorProducing<float>, ICalibrator>> which is a step forward in solving the problem, but is not yet enough.

                As stated, in both cases, a cast to the original type would return null. In general, it would be expected that the user tries to make that cast in order to use PFI, failing to accomplish it.

                This problem would be solved if the loaded model actually had a lastTransformer of the original type, or something castable to it.

                Workaround

                Based on this comment made by @yaeldekel I've just made this working sample of using PFI with a binary prediction transformer loaded from disk. It is pretty much the same as the original sample, only that it works with a model loaded from disk.

                The key of the workaround is that the user should cast the lastTransformer not into a binary prediction transformer but rather into a ISingleFeaturePredictionTransformer<object>, and then do a series of casts to get whatever other object s/he may want to get from inside the lastTransformer.

                In the sample I've just provided it works pretty much in this way:

                var linearPredictor = (loadedmodel as TransformerChain<ITransformer>).LastTransformer as ISingleFeaturePredictionTransformer<object>;
                var predictorModel = linearPredictor.Model as CalibratedModelParametersBase;
                var predictorSubModel = predictorModel.SubModel as LinearBinaryModelParameters;
                

                Notice that this workaround worked even in ML.Net 1.3, and also works with the changes that I introduced in 1.4.0 preview 2.

                Notice that a similar workaround might help a user that tries to use PFI with any kind of prediction transformer loaded from disk. This would come useful if the user, for whatever reason, can not extract the linearPredictor by casting to the same type used in the original model.

                Cause of the Problem

                There are 3 main points that are related to the cause of this problem, all of which pertain the Calibrator.cs file and aren't related to the binary prediction transformer itself:

                1. Unexpectedly, when loading a ParameterMixingCalibratedModelParameters<> its Create method isn't called. I discovered this while debugging, and what actually happens is that, during loading, inside the CreateInstanceCore method, it first looks for a constructor, and so it calls the constructor of ParameterMixingCalibratedModelParameters<> instead of the Create method.
                2. Currently, when loading a ParameterMixingCalibratedModelParameters<> model, a ParameterMixingCalibratedModelParameters<IPredictorProducing<float>, ICalibrator> is always loaded, no matter what the actual submodel and calibrator are. This doesn't change by fixing point 1). This point is similar to the original problem found on the prediction transformers, which I fixed in my last pull request; using a similar approach in this case would fix this point... that is, loading first the submodel and calibrator to then create a generic type at runtime with the correct parameter types.
                3. When fiting the model (i.e. before even saving it or loading it) the SdcaLogisticRegressionBinaryTrainer creates a predictor of type ParameterMixingCalibratedModelParameters<LinearBinaryModelParameters, PlattCalibrator> (which I will now refer to as "PMCMP") but returns it as a CalibratedModelParametersBase<LinearBinaryModelParameters, PlattCalibrator> (let's call it "CMPB") this then is what makes the last transformer of the model to be a BinaryPredictionTransformer<CMPB> whereas the internal model of the last transformer is actually a PMCMP. When saving it to disk, it's saved as a PMCMP (i.e. it's saved using a LoaderSignature of "PMixCaliPredExec"), so when loading occurs, it calls the constructor of PMCMP but it doesn't cast it to a CMPB. This is different from the problem fixed in my last pull request; there, if a Regression prediction transformer was saved, then we expected to load a regression prediction transformer... whereas in here if a BPT is saved we actually want to load a PMCMP with the correct type parameters, but actually create a BPT where the CMPB should also have the correct type parameters.

                Trying to solve the problem

                So far I've been able to solve problems 1) and 2) described above, but after trying out different approaches I haven't been able to solve problem 3). To solve those problems I've changed different things in the Calibrator.cs file. My attempt to solve this problem can be found in PR #4306

                With those changes (along with the ones of my last PR), the loaded model's last transformer becomes a BinaryPredictionTransformer<ParameterMixingCalibratedModelParameters<LinearBinaryModelParameters, PlattCalibrator>>. Notice that even here a cast to BPT<CMPB> would be null, so it doesn't solve the problem. Also notice that since PMCMP is an internal class the user wouldn't be able to cast the last transformer to BPT<PMCMP> either, since s/he wouldn't have access to that class.

                Further problems

                Here I've explained the specific case of loading a BPT<CMPB> with the specific problems that arise in CMPB and PMCMP classes because that is what is used in the sample of PFI with BPT, and in the tests of PFI with BPT. It could be possible that the problems here described are also present in other classes (for example in the other classes of Calibrator.cs) but they might not become a problem unless the user tries to access the last transformer of a model loaded from disk. In such a case the described workaround might help.

                Activity

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

                Metadata

                Metadata

                Assignees

                Labels

                No labels
                No labels

                Type

                No type

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions