forked from moreo/QuaPy
merging PR; I have taken this opportunity to refactor some issues I didnt like, including the normalization of prevalence vectors, and improving the documentation here and there
This commit is contained in:
parent
36ac6db27d
commit
aa894a3472
97
TODO.txt
97
TODO.txt
|
@ -1,97 +0,0 @@
|
|||
check sphinks doc for enumerations (for example, the doc for ACC)
|
||||
|
||||
ensembles seem to be broken; they have an internal model selection which takes the parameters, but since quapy now
|
||||
works with protocols it would need to know the validation set in order to pass something like
|
||||
"protocol: APP(val, etc.)"
|
||||
sample_size should not be mandatory when qp.environ['SAMPLE_SIZE'] has been specified
|
||||
clean all the cumbersome methods that have to be implemented for new quantifiers (e.g., n_classes_ prop, etc.)
|
||||
make truly parallel the GridSearchQ
|
||||
make more examples in the "examples" directory
|
||||
merge with master, because I had to fix some problems with QuaNet due to an issue notified via GitHub!
|
||||
added cross_val_predict in qp.model_selection (i.e., a cross_val_predict for quantification) --would be nice to have
|
||||
it parallelized
|
||||
|
||||
check the OneVsAll module(s)
|
||||
|
||||
check the set_params de neural.py, because the separation of estimator__<param> is not implemented; see also
|
||||
__check_params_colision
|
||||
|
||||
HDy can be customized so that the number of bins is specified, instead of explored within the fit method
|
||||
|
||||
Packaging:
|
||||
==========================================
|
||||
Document methods with paper references
|
||||
unit-tests
|
||||
clean wiki_examples!
|
||||
|
||||
Refactor:
|
||||
==========================================
|
||||
Unify ThresholdOptimization methods, as an extension of PACC (and not ACC), the fit methods are almost identical and
|
||||
use a prob classifier (take into account that PACC uses pcc internally, whereas the threshold methods use cc
|
||||
instead). The fit method of ACC and PACC has a block for estimating the validation estimates that should be unified
|
||||
as well...
|
||||
Refactor protocols. APP and NPP related functionalities are duplicated in functional, LabelledCollection, and evaluation
|
||||
|
||||
|
||||
New features:
|
||||
==========================================
|
||||
Add "measures for evaluating ordinal"?
|
||||
Add datasets for topic.
|
||||
Do we want to cover cross-lingual quantification natively in QuaPy, or does it make more sense as an application on top?
|
||||
|
||||
Current issues:
|
||||
==========================================
|
||||
Revise the class structure of quantification methods and the methods they inherit... There is some confusion regarding
|
||||
methods isbinary, isprobabilistic, and the like. The attribute "learner_" in aggregative quantifiers is also
|
||||
confusing, since there is a getter and a setter.
|
||||
Remove the "deep" in get_params. There is no real compatibility with scikit-learn as for now.
|
||||
SVMperf-based learners do not remove temp files in __del__?
|
||||
In binary quantification (hp, kindle, imdb) we used F1 in the minority class (which in kindle and hp happens to be the
|
||||
negative class). This is not covered in this new implementation, in which the binary case is not treated as such, but as
|
||||
an instance of single-label with 2 labels. Check
|
||||
Add automatic reindex of class labels in LabelledCollection (currently, class indexes should be ordered and with no gaps)
|
||||
OVR I believe is currently tied to aggregative methods. We should provide a general interface also for general quantifiers
|
||||
Currently, being "binary" only adds one checker; we should figure out how to impose the check to be automatically performed
|
||||
Add random seed management to support replicability (see temp_seed in util.py).
|
||||
GridSearchQ is not trully parallelized. It only parallelizes on the predictions.
|
||||
In the context of a quantifier (e.g., QuaNet or CC), the parameters of the learner should be prefixed with "estimator__",
|
||||
in QuaNet this is resolved with a __check_params_colision, but this should be improved. It might be cumbersome to
|
||||
impose the "estimator__" prefix for, e.g., quantifiers like CC though... This should be changed everywhere...
|
||||
QuaNet needs refactoring. The base quantifiers ACC and PACC receive val_data with instances already transformed. This
|
||||
issue is due to a bad design.
|
||||
|
||||
Improvements:
|
||||
==========================================
|
||||
Explore the hyperparameter "number of bins" in HDy
|
||||
Rename EMQ to SLD ?
|
||||
Parallelize the kFCV in ACC and PACC?
|
||||
Parallelize model selection trainings
|
||||
We might want to think of (improving and) adding the class Tabular (it is defined and used on branch tweetsent). A more
|
||||
recent version is in the project ql4facct. This class is meant to generate latex tables from results (highligting
|
||||
best results, computing statistical tests, colouring cells, producing rankings, producing averages, etc.). Trying
|
||||
to generate tables is typically a bad idea, but in this specific case we do have pretty good control of what an
|
||||
experiment looks like. (Do we want to abstract experimental results? this could be useful not only for tables but
|
||||
also for plots).
|
||||
Add proper logging system. Currently we use print
|
||||
It might be good to simplify the number of methods that have to be implemented for any new Quantifier. At the moment,
|
||||
there are many functions like get_params, set_params, and, specially, @property classes_, which are cumbersome to
|
||||
implement for quick experiments. A possible solution is to impose get_params and set_params only in cases in which
|
||||
the model extends some "ModelSelectable" interface only. The classes_ should have a default implementation.
|
||||
|
||||
Checks:
|
||||
==========================================
|
||||
How many times is the system of equations for ACC and PACC not solved? How many times is it clipped? Do they sum up
|
||||
to one always?
|
||||
Re-check how hyperparameters from the quantifier and hyperparameters from the classifier (in aggregative quantifiers)
|
||||
is handled. In scikit-learn the hyperparameters from a wrapper method are indicated directly whereas the hyperparams
|
||||
from the internal learner are prefixed with "estimator__". In QuaPy, combinations having to do with the classifier
|
||||
can be computed at the begining, and then in an internal loop the hyperparams of the quantifier can be explored,
|
||||
passing fit_learner=False.
|
||||
Re-check Ensembles. As for now, they are strongly tied to aggregative quantifiers.
|
||||
Re-think the environment variables. Maybe add new ones (like, for example, parameters for the plots)
|
||||
Do we want to wrap prevalences (currently simple np.ndarray) as a class? This might be convenient for some interfaces
|
||||
(e.g., for specifying artificial prevalences in samplings, for printing them -- currently supported through
|
||||
F.strprev(), etc.). This might however add some overload, and prevent/difficult post processing with numpy.
|
||||
Would be nice to get a better integration with sklearn.
|
||||
|
||||
|
|
@ -116,8 +116,6 @@
|
|||
<li><a href="quapy.html#quapy.error.acce">acce() (in module quapy.error)</a>
|
||||
</li>
|
||||
<li><a href="quapy.data.html#quapy.data.preprocessing.IndexTransformer.add_word">add_word() (quapy.data.preprocessing.IndexTransformer method)</a>
|
||||
</li>
|
||||
<li><a href="quapy.html#quapy.functional.adjusted_quantification">adjusted_quantification() (in module quapy.functional)</a>
|
||||
</li>
|
||||
<li><a href="quapy.method.html#quapy.method.aggregative.AdjustedClassifyAndCount">AdjustedClassifyAndCount (in module quapy.method.aggregative)</a>
|
||||
</li>
|
||||
|
@ -297,9 +295,7 @@
|
|||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="quapy.html#quapy.functional.clip_prevalence">clip_prevalence() (in module quapy.functional)</a>
|
||||
</li>
|
||||
<li><a href="quapy.method.html#quapy.method.aggregative.ACC.CLIPPING">CLIPPING (quapy.method.aggregative.ACC attribute)</a>
|
||||
<li><a href="quapy.html#quapy.functional.clip">clip() (in module quapy.functional)</a>
|
||||
</li>
|
||||
<li><a href="quapy.classification.html#quapy.classification.neural.CNNnet">CNNnet (class in quapy.classification.neural)</a>
|
||||
</li>
|
||||
|
@ -317,6 +313,8 @@
|
|||
<li><a href="quapy.method.html#quapy.method._threshold_optim.X.condition">(quapy.method._threshold_optim.X method)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="quapy.html#quapy.functional.condsoftmax">condsoftmax() (in module quapy.functional)</a>
|
||||
</li>
|
||||
<li><a href="quapy.html#quapy.model_selection.ConfigStatus">ConfigStatus (class in quapy.model_selection)</a>
|
||||
</li>
|
||||
<li><a href="quapy.data.html#quapy.data.base.LabelledCollection.counts">counts() (quapy.data.base.LabelledCollection method)</a>
|
||||
|
@ -647,18 +645,20 @@
|
|||
<h2 id="L">L</h2>
|
||||
<table style="width: 100%" class="indextable genindextable"><tr>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="quapy.html#quapy.functional.l1_norm">l1_norm() (in module quapy.functional)</a>
|
||||
</li>
|
||||
<li><a href="quapy.data.html#quapy.data.base.LabelledCollection">LabelledCollection (class in quapy.data.base)</a>
|
||||
</li>
|
||||
<li><a href="quapy.html#quapy.functional.linear_search">linear_search() (in module quapy.functional)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="quapy.data.html#quapy.data.base.Dataset.load">load() (quapy.data.base.Dataset class method)</a>
|
||||
|
||||
<ul>
|
||||
<li><a href="quapy.data.html#quapy.data.base.LabelledCollection.load">(quapy.data.base.LabelledCollection class method)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="quapy.classification.html#quapy.classification.methods.LowRankLogisticRegression">LowRankLogisticRegression (class in quapy.classification.methods)</a>
|
||||
</li>
|
||||
<li><a href="quapy.classification.html#quapy.classification.neural.LSTMnet">LSTMnet (class in quapy.classification.neural)</a>
|
||||
|
@ -672,8 +672,6 @@
|
|||
<li><a href="quapy.html#quapy.error.mae">mae() (in module quapy.error)</a>
|
||||
</li>
|
||||
<li><a href="quapy.method.html#quapy.method._neural.mae_loss">mae_loss() (in module quapy.method._neural)</a>
|
||||
</li>
|
||||
<li><a href="quapy.html#quapy.functional.map_onto_probability_simplex">map_onto_probability_simplex() (in module quapy.functional)</a>
|
||||
</li>
|
||||
<li><a href="quapy.html#quapy.util.map_parallel">map_parallel() (in module quapy.util)</a>
|
||||
</li>
|
||||
|
@ -800,10 +798,10 @@
|
|||
</li>
|
||||
<li><a href="quapy.method.html#quapy.method.base.newOneVsAll">newOneVsAll() (in module quapy.method.base)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="quapy.method.html#quapy.method.aggregative.newSVMAE">newSVMAE() (in module quapy.method.aggregative)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="quapy.method.html#quapy.method.aggregative.newSVMKLD">newSVMKLD() (in module quapy.method.aggregative)</a>
|
||||
</li>
|
||||
<li><a href="quapy.method.html#quapy.method.aggregative.newSVMQ">newSVMQ() (in module quapy.method.aggregative)</a>
|
||||
|
@ -811,6 +809,8 @@
|
|||
<li><a href="quapy.method.html#quapy.method.aggregative.newSVMRAE">newSVMRAE() (in module quapy.method.aggregative)</a>
|
||||
</li>
|
||||
<li><a href="quapy.html#quapy.error.nkld">nkld() (in module quapy.error)</a>
|
||||
</li>
|
||||
<li><a href="quapy.method.html#quapy.method.aggregative.ACC.NORMALIZATIONS">NORMALIZATIONS (quapy.method.aggregative.ACC attribute)</a>
|
||||
</li>
|
||||
<li><a href="quapy.html#quapy.functional.normalize_prevalence">normalize_prevalence() (in module quapy.functional)</a>
|
||||
</li>
|
||||
|
@ -907,6 +907,8 @@
|
|||
<li><a href="quapy.method.html#quapy.method.aggregative.ProbabilisticAdjustedClassifyAndCount">ProbabilisticAdjustedClassifyAndCount (in module quapy.method.aggregative)</a>
|
||||
</li>
|
||||
<li><a href="quapy.method.html#quapy.method.aggregative.ProbabilisticClassifyAndCount">ProbabilisticClassifyAndCount (in module quapy.method.aggregative)</a>
|
||||
</li>
|
||||
<li><a href="quapy.html#quapy.functional.projection_simplex_sort">projection_simplex_sort() (in module quapy.functional)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
</tr></table>
|
||||
|
@ -1225,8 +1227,12 @@
|
|||
<li><a href="quapy.method.html#quapy.method.aggregative.SMM">SMM (class in quapy.method.aggregative)</a>
|
||||
</li>
|
||||
<li><a href="quapy.html#quapy.error.smooth">smooth() (in module quapy.error)</a>
|
||||
</li>
|
||||
<li><a href="quapy.html#quapy.functional.softmax">softmax() (in module quapy.functional)</a>
|
||||
</li>
|
||||
<li><a href="quapy.html#quapy.functional.solve_adjustment">solve_adjustment() (in module quapy.functional)</a>
|
||||
</li>
|
||||
<li><a href="quapy.html#quapy.functional.solve_adjustment_binary">solve_adjustment_binary() (in module quapy.functional)</a>
|
||||
</li>
|
||||
<li><a href="quapy.method.html#quapy.method.aggregative.ACC.SOLVERS">SOLVERS (quapy.method.aggregative.ACC attribute)</a>
|
||||
</li>
|
||||
|
@ -1265,6 +1271,8 @@
|
|||
<li><a href="quapy.method.html#quapy.method._threshold_optim.T50">T50 (class in quapy.method._threshold_optim)</a>
|
||||
</li>
|
||||
<li><a href="quapy.html#quapy.util.temp_seed">temp_seed() (in module quapy.util)</a>
|
||||
</li>
|
||||
<li><a href="quapy.html#quapy.functional.ternary_search">ternary_search() (in module quapy.functional)</a>
|
||||
</li>
|
||||
<li><a href="quapy.data.html#quapy.data.preprocessing.text2tfidf">text2tfidf() (in module quapy.data.preprocessing)</a>
|
||||
</li>
|
||||
|
|
|
@ -263,8 +263,8 @@
|
|||
<li class="toctree-l5"><a class="reference internal" href="quapy.method.html#submodules">Submodules</a></li>
|
||||
<li class="toctree-l5"><a class="reference internal" href="quapy.method.html#module-quapy.method.aggregative">quapy.method.aggregative module</a><ul>
|
||||
<li class="toctree-l6"><a class="reference internal" href="quapy.method.html#quapy.method.aggregative.ACC"><code class="docutils literal notranslate"><span class="pre">ACC</span></code></a><ul>
|
||||
<li class="toctree-l7"><a class="reference internal" href="quapy.method.html#quapy.method.aggregative.ACC.CLIPPING"><code class="docutils literal notranslate"><span class="pre">ACC.CLIPPING</span></code></a></li>
|
||||
<li class="toctree-l7"><a class="reference internal" href="quapy.method.html#quapy.method.aggregative.ACC.METHODS"><code class="docutils literal notranslate"><span class="pre">ACC.METHODS</span></code></a></li>
|
||||
<li class="toctree-l7"><a class="reference internal" href="quapy.method.html#quapy.method.aggregative.ACC.NORMALIZATIONS"><code class="docutils literal notranslate"><span class="pre">ACC.NORMALIZATIONS</span></code></a></li>
|
||||
<li class="toctree-l7"><a class="reference internal" href="quapy.method.html#quapy.method.aggregative.ACC.SOLVERS"><code class="docutils literal notranslate"><span class="pre">ACC.SOLVERS</span></code></a></li>
|
||||
<li class="toctree-l7"><a class="reference internal" href="quapy.method.html#quapy.method.aggregative.ACC.aggregate"><code class="docutils literal notranslate"><span class="pre">ACC.aggregate()</span></code></a></li>
|
||||
<li class="toctree-l7"><a class="reference internal" href="quapy.method.html#quapy.method.aggregative.ACC.aggregation_fit"><code class="docutils literal notranslate"><span class="pre">ACC.aggregation_fit()</span></code></a></li>
|
||||
|
@ -564,24 +564,28 @@
|
|||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#module-quapy.functional">quapy.functional module</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.HellingerDistance"><code class="docutils literal notranslate"><span class="pre">HellingerDistance()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.TopsoeDistance"><code class="docutils literal notranslate"><span class="pre">TopsoeDistance()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.adjusted_quantification"><code class="docutils literal notranslate"><span class="pre">adjusted_quantification()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.argmin_prevalence"><code class="docutils literal notranslate"><span class="pre">argmin_prevalence()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.as_binary_prevalence"><code class="docutils literal notranslate"><span class="pre">as_binary_prevalence()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.check_prevalence_vector"><code class="docutils literal notranslate"><span class="pre">check_prevalence_vector()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.clip_prevalence"><code class="docutils literal notranslate"><span class="pre">clip_prevalence()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.clip"><code class="docutils literal notranslate"><span class="pre">clip()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.condsoftmax"><code class="docutils literal notranslate"><span class="pre">condsoftmax()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.counts_from_labels"><code class="docutils literal notranslate"><span class="pre">counts_from_labels()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.get_divergence"><code class="docutils literal notranslate"><span class="pre">get_divergence()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.get_nprevpoints_approximation"><code class="docutils literal notranslate"><span class="pre">get_nprevpoints_approximation()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.l1_norm"><code class="docutils literal notranslate"><span class="pre">l1_norm()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.linear_search"><code class="docutils literal notranslate"><span class="pre">linear_search()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.map_onto_probability_simplex"><code class="docutils literal notranslate"><span class="pre">map_onto_probability_simplex()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.normalize_prevalence"><code class="docutils literal notranslate"><span class="pre">normalize_prevalence()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.num_prevalence_combinations"><code class="docutils literal notranslate"><span class="pre">num_prevalence_combinations()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.optim_minimize"><code class="docutils literal notranslate"><span class="pre">optim_minimize()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.prevalence_from_labels"><code class="docutils literal notranslate"><span class="pre">prevalence_from_labels()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.prevalence_from_probabilities"><code class="docutils literal notranslate"><span class="pre">prevalence_from_probabilities()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.prevalence_linspace"><code class="docutils literal notranslate"><span class="pre">prevalence_linspace()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.projection_simplex_sort"><code class="docutils literal notranslate"><span class="pre">projection_simplex_sort()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.softmax"><code class="docutils literal notranslate"><span class="pre">softmax()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.solve_adjustment"><code class="docutils literal notranslate"><span class="pre">solve_adjustment()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.solve_adjustment_binary"><code class="docutils literal notranslate"><span class="pre">solve_adjustment_binary()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.strprev"><code class="docutils literal notranslate"><span class="pre">strprev()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.ternary_search"><code class="docutils literal notranslate"><span class="pre">ternary_search()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.uniform_prevalence_sampling"><code class="docutils literal notranslate"><span class="pre">uniform_prevalence_sampling()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quapy.html#quapy.functional.uniform_simplex_sampling"><code class="docutils literal notranslate"><span class="pre">uniform_simplex_sampling()</span></code></a></li>
|
||||
</ul>
|
||||
|
|
|
@ -153,24 +153,28 @@
|
|||
<li class="toctree-l2"><a class="reference internal" href="quapy.html#module-quapy.functional">quapy.functional module</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.HellingerDistance"><code class="docutils literal notranslate"><span class="pre">HellingerDistance()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.TopsoeDistance"><code class="docutils literal notranslate"><span class="pre">TopsoeDistance()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.adjusted_quantification"><code class="docutils literal notranslate"><span class="pre">adjusted_quantification()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.argmin_prevalence"><code class="docutils literal notranslate"><span class="pre">argmin_prevalence()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.as_binary_prevalence"><code class="docutils literal notranslate"><span class="pre">as_binary_prevalence()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.check_prevalence_vector"><code class="docutils literal notranslate"><span class="pre">check_prevalence_vector()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.clip_prevalence"><code class="docutils literal notranslate"><span class="pre">clip_prevalence()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.clip"><code class="docutils literal notranslate"><span class="pre">clip()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.condsoftmax"><code class="docutils literal notranslate"><span class="pre">condsoftmax()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.counts_from_labels"><code class="docutils literal notranslate"><span class="pre">counts_from_labels()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.get_divergence"><code class="docutils literal notranslate"><span class="pre">get_divergence()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.get_nprevpoints_approximation"><code class="docutils literal notranslate"><span class="pre">get_nprevpoints_approximation()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.l1_norm"><code class="docutils literal notranslate"><span class="pre">l1_norm()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.linear_search"><code class="docutils literal notranslate"><span class="pre">linear_search()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.map_onto_probability_simplex"><code class="docutils literal notranslate"><span class="pre">map_onto_probability_simplex()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.normalize_prevalence"><code class="docutils literal notranslate"><span class="pre">normalize_prevalence()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.num_prevalence_combinations"><code class="docutils literal notranslate"><span class="pre">num_prevalence_combinations()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.optim_minimize"><code class="docutils literal notranslate"><span class="pre">optim_minimize()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.prevalence_from_labels"><code class="docutils literal notranslate"><span class="pre">prevalence_from_labels()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.prevalence_from_probabilities"><code class="docutils literal notranslate"><span class="pre">prevalence_from_probabilities()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.prevalence_linspace"><code class="docutils literal notranslate"><span class="pre">prevalence_linspace()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.projection_simplex_sort"><code class="docutils literal notranslate"><span class="pre">projection_simplex_sort()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.softmax"><code class="docutils literal notranslate"><span class="pre">softmax()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.solve_adjustment"><code class="docutils literal notranslate"><span class="pre">solve_adjustment()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.solve_adjustment_binary"><code class="docutils literal notranslate"><span class="pre">solve_adjustment_binary()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.strprev"><code class="docutils literal notranslate"><span class="pre">strprev()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.ternary_search"><code class="docutils literal notranslate"><span class="pre">ternary_search()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.uniform_prevalence_sampling"><code class="docutils literal notranslate"><span class="pre">uniform_prevalence_sampling()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quapy.html#quapy.functional.uniform_simplex_sampling"><code class="docutils literal notranslate"><span class="pre">uniform_simplex_sampling()</span></code></a></li>
|
||||
</ul>
|
||||
|
|
Binary file not shown.
File diff suppressed because one or more lines are too long
|
@ -104,7 +104,7 @@
|
|||
<span id="quapy-method-aggregative-module"></span><h2>quapy.method.aggregative module<a class="headerlink" href="#module-quapy.method.aggregative" title="Permalink to this heading"></a></h2>
|
||||
<dl class="py class">
|
||||
<dt class="sig sig-object py" id="quapy.method.aggregative.ACC">
|
||||
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">quapy.method.aggregative.</span></span><span class="sig-name descname"><span class="pre">ACC</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">classifier</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">BaseEstimator</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">val_split</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">5</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">solver</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Literal</span><span class="p"><span class="pre">[</span></span><span class="s"><span class="pre">'minimize'</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="s"><span class="pre">'exact'</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="s"><span class="pre">'exact-raise'</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="s"><span class="pre">'exact-cc'</span></span><span class="p"><span class="pre">]</span></span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">'minimize'</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">method</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Literal</span><span class="p"><span class="pre">[</span></span><span class="s"><span class="pre">'inversion'</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="s"><span class="pre">'invariant-ratio'</span></span><span class="p"><span class="pre">]</span></span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">'inversion'</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">clipping</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Literal</span><span class="p"><span class="pre">[</span></span><span class="s"><span class="pre">'clip'</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="s"><span class="pre">'none'</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="s"><span class="pre">'project'</span></span><span class="p"><span class="pre">]</span></span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">'clip'</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">n_jobs</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/quapy/method/aggregative.html#ACC"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#quapy.method.aggregative.ACC" title="Permalink to this definition"></a></dt>
|
||||
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">quapy.method.aggregative.</span></span><span class="sig-name descname"><span class="pre">ACC</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">classifier</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">BaseEstimator</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">val_split</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">5</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">solver</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Literal</span><span class="p"><span class="pre">[</span></span><span class="s"><span class="pre">'minimize'</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="s"><span class="pre">'exact'</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="s"><span class="pre">'exact-raise'</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="s"><span class="pre">'exact-cc'</span></span><span class="p"><span class="pre">]</span></span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">'minimize'</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">method</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Literal</span><span class="p"><span class="pre">[</span></span><span class="s"><span class="pre">'inversion'</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="s"><span class="pre">'invariant-ratio'</span></span><span class="p"><span class="pre">]</span></span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">'inversion'</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">norm</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Literal</span><span class="p"><span class="pre">[</span></span><span class="s"><span class="pre">'clip'</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="s"><span class="pre">'mapsimplex'</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="s"><span class="pre">'condsoftmax'</span></span><span class="p"><span class="pre">]</span></span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">'clip'</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">n_jobs</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/quapy/method/aggregative.html#ACC"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#quapy.method.aggregative.ACC" title="Permalink to this definition"></a></dt>
|
||||
<dd><p>Bases: <a class="reference internal" href="#quapy.method.aggregative.AggregativeCrispQuantifier" title="quapy.method.aggregative.AggregativeCrispQuantifier"><code class="xref py py-class docutils literal notranslate"><span class="pre">AggregativeCrispQuantifier</span></code></a></p>
|
||||
<p><a class="reference external" href="https://link.springer.com/article/10.1007/s10618-008-0097-y">Adjusted Classify & Count</a>,
|
||||
the “adjusted” variant of <a class="reference internal" href="#quapy.method.aggregative.CC" title="quapy.method.aggregative.CC"><code class="xref py py-class docutils literal notranslate"><span class="pre">CC</span></code></a>, that corrects the predictions of CC
|
||||
|
@ -122,23 +122,31 @@ Alternatively, this set can be specified at fit time by indicating the exact set
|
|||
on which the predictions are to be generated.</p></li>
|
||||
<li><p><strong>method</strong> (<em>str</em>) – <p>adjustment method to be used:</p>
|
||||
<ul>
|
||||
<li><p>’inversion’: matrix inversion method based on the matrix equality <span class="math notranslate nohighlight">\(P(C)=P(C|Y)P(Y)\)</span>, which tries to invert <span class="math notranslate nohighlight">\(P(C|Y)\)</span> matrix.</p></li>
|
||||
<li><p>’invariant-ratio’: invariant ratio estimator of <a class="reference external" href="https://jmlr.org/papers/v20/18-456.html">Vaz et al. 2018</a>, which replaces the last equation with the normalization condition.</p></li>
|
||||
<li><p>’inversion’: matrix inversion method based on the matrix equality <span class="math notranslate nohighlight">\(P(C)=P(C|Y)P(Y)\)</span>,
|
||||
which tries to invert <span class="math notranslate nohighlight">\(P(C|Y)\)</span> matrix.</p></li>
|
||||
<li><p>’invariant-ratio’: invariant ratio estimator of <a class="reference external" href="https://jmlr.org/papers/v20/18-456.html">Vaz et al. 2018</a>,
|
||||
which replaces the last equation with the normalization condition.</p></li>
|
||||
</ul>
|
||||
</p></li>
|
||||
<li><p><strong>solver</strong> (<em>str</em>) – <p>indicates the method to use for solving the system of linear equations. Valid options are:</p>
|
||||
<ul>
|
||||
<li><p>’exact-raise’: tries to solve the system using matrix inversion. Raises an error if the matrix has rank strictly less than <cite>n_classes</cite>.</p></li>
|
||||
<li><p>’exact-cc’: if the matrix is not of full rank, returns <cite>p_c</cite> as the estimates, which corresponds to no adjustment (i.e., the classify and count method. See <a class="reference internal" href="#quapy.method.aggregative.CC" title="quapy.method.aggregative.CC"><code class="xref py py-class docutils literal notranslate"><span class="pre">quapy.method.aggregative.CC</span></code></a>)</p></li>
|
||||
<li><p>’exact-raise’: tries to solve the system using matrix inversion. Raises an error if the matrix has rank
|
||||
strictly less than <cite>n_classes</cite>.</p></li>
|
||||
<li><p>’exact-cc’: if the matrix is not of full rank, returns <cite>p_c</cite> as the estimates, which corresponds to
|
||||
no adjustment (i.e., the classify and count method. See <a class="reference internal" href="#quapy.method.aggregative.CC" title="quapy.method.aggregative.CC"><code class="xref py py-class docutils literal notranslate"><span class="pre">quapy.method.aggregative.CC</span></code></a>)</p></li>
|
||||
<li><p>’exact’: deprecated, defaults to ‘exact-cc’</p></li>
|
||||
<li><p>’minimize’: minimizes the L2 norm of <span class="math notranslate nohighlight">\(|Ax-B|\)</span>. This one generally works better, and is the default parameter. More details about this can be consulted in <a class="reference external" href="https://lq-2022.github.io/proceedings/CompleteVolume.pdf">Bunse, M. “On Multi-Class Extensions of Adjusted Classify and Count”, on proceedings of the 2nd International Workshop on Learning to Quantify: Methods and Applications (LQ 2022), ECML/PKDD 2022, Grenoble (France)</a>.</p></li>
|
||||
<li><p>’minimize’: minimizes the L2 norm of <span class="math notranslate nohighlight">\(|Ax-B|\)</span>. This one generally works better, and is the
|
||||
default parameter. More details about this can be consulted in <a class="reference external" href="https://lq-2022.github.io/proceedings/CompleteVolume.pdf">Bunse, M. “On Multi-Class Extensions of
|
||||
Adjusted Classify and Count”, on proceedings of the 2nd International Workshop on Learning to Quantify:
|
||||
Methods and Applications (LQ 2022), ECML/PKDD 2022, Grenoble (France)</a>.</p></li>
|
||||
</ul>
|
||||
</p></li>
|
||||
<li><p><strong>clipping</strong> (<em>str</em>) – <p>the method to use for normalization.</p>
|
||||
<li><p><strong>norm</strong> (<em>str</em>) – <p>the method to use for normalization.</p>
|
||||
<ul>
|
||||
<li><p>If <cite>None</cite> or <cite>“none”</cite>, no normalization is performed.</p></li>
|
||||
<li><p>If <cite>“clip”</cite>, the values are clipped to the range [0,1] and normalized, so they sum up to 1.</p></li>
|
||||
<li><p>If <cite>“project”</cite>, the values are projected onto the probability simplex.</p></li>
|
||||
<li><p><cite>clip</cite>, the values are clipped to the range [0,1] and then L1-normalized.</p></li>
|
||||
<li><p><cite>mapsimplex</cite> projects vectors onto the probability simplex. This implementation relies on
|
||||
<a class="reference external" href="https://gist.github.com/mblondel/6f3b7aaad90606b98f71">Mathieu Blondel’s projection_simplex_sort</a></p></li>
|
||||
<li><p><cite>condsoftmax</cite>, applies a softmax normalization only to prevalence vectors that lie outside the simplex</p></li>
|
||||
</ul>
|
||||
</p></li>
|
||||
<li><p><strong>n_jobs</strong> – number of parallel workers</p></li>
|
||||
|
@ -146,13 +154,13 @@ on which the predictions are to be generated.</p></li>
|
|||
</dd>
|
||||
</dl>
|
||||
<dl class="py attribute">
|
||||
<dt class="sig sig-object py" id="quapy.method.aggregative.ACC.CLIPPING">
|
||||
<span class="sig-name descname"><span class="pre">CLIPPING</span></span><em class="property"><span class="w"> </span><span class="p"><span class="pre">=</span></span><span class="w"> </span><span class="pre">['clip',</span> <span class="pre">'none',</span> <span class="pre">'project',</span> <span class="pre">None]</span></em><a class="headerlink" href="#quapy.method.aggregative.ACC.CLIPPING" title="Permalink to this definition"></a></dt>
|
||||
<dt class="sig sig-object py" id="quapy.method.aggregative.ACC.METHODS">
|
||||
<span class="sig-name descname"><span class="pre">METHODS</span></span><em class="property"><span class="w"> </span><span class="p"><span class="pre">=</span></span><span class="w"> </span><span class="pre">['inversion',</span> <span class="pre">'invariant-ratio']</span></em><a class="headerlink" href="#quapy.method.aggregative.ACC.METHODS" title="Permalink to this definition"></a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
<dt class="sig sig-object py" id="quapy.method.aggregative.ACC.METHODS">
|
||||
<span class="sig-name descname"><span class="pre">METHODS</span></span><em class="property"><span class="w"> </span><span class="p"><span class="pre">=</span></span><span class="w"> </span><span class="pre">['inversion',</span> <span class="pre">'invariant-ratio']</span></em><a class="headerlink" href="#quapy.method.aggregative.ACC.METHODS" title="Permalink to this definition"></a></dt>
|
||||
<dt class="sig sig-object py" id="quapy.method.aggregative.ACC.NORMALIZATIONS">
|
||||
<span class="sig-name descname"><span class="pre">NORMALIZATIONS</span></span><em class="property"><span class="w"> </span><span class="p"><span class="pre">=</span></span><span class="w"> </span><span class="pre">['clip',</span> <span class="pre">'mapsimplex',</span> <span class="pre">'condsoftmax',</span> <span class="pre">None]</span></em><a class="headerlink" href="#quapy.method.aggregative.ACC.NORMALIZATIONS" title="Permalink to this definition"></a></dt>
|
||||
<dd></dd></dl>
|
||||
|
||||
<dl class="py attribute">
|
||||
|
@ -212,7 +220,7 @@ document that belongs to yj ends up being classified as belonging to yi</p>
|
|||
<dt class="sig sig-object py" id="quapy.method.aggregative.ACC.newInvariantRatioEstimation">
|
||||
<em class="property"><span class="pre">classmethod</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">newInvariantRatioEstimation</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">classifier</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">BaseEstimator</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">val_split</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">5</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">n_jobs</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/quapy/method/aggregative.html#ACC.newInvariantRatioEstimation"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#quapy.method.aggregative.ACC.newInvariantRatioEstimation" title="Permalink to this definition"></a></dt>
|
||||
<dd><p>Constructs a quantifier that implements the Invariant Ratio Estimator of
|
||||
<cite>Vaz et al. 2018 <https://jmlr.org/papers/v20/18-456.html>_</cite>. This amounts
|
||||
<a class="reference external" href="https://jmlr.org/papers/v20/18-456.html">Vaz et al. 2018</a>. This amounts
|
||||
to setting method to ‘invariant-ratio’ and clipping to ‘project’.</p>
|
||||
<dl class="field-list simple">
|
||||
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
|
||||
|
@ -1046,7 +1054,7 @@ probabilities are independent of each other, meaning that, in general, they do n
|
|||
|
||||
<dl class="py class">
|
||||
<dt class="sig sig-object py" id="quapy.method.aggregative.PACC">
|
||||
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">quapy.method.aggregative.</span></span><span class="sig-name descname"><span class="pre">PACC</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">classifier</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">BaseEstimator</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">val_split</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">5</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">solver</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Literal</span><span class="p"><span class="pre">[</span></span><span class="s"><span class="pre">'minimize'</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="s"><span class="pre">'exact'</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="s"><span class="pre">'exact-raise'</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="s"><span class="pre">'exact-cc'</span></span><span class="p"><span class="pre">]</span></span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">'minimize'</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">method</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Literal</span><span class="p"><span class="pre">[</span></span><span class="s"><span class="pre">'inversion'</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="s"><span class="pre">'invariant-ratio'</span></span><span class="p"><span class="pre">]</span></span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">'inversion'</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">clipping</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Literal</span><span class="p"><span class="pre">[</span></span><span class="s"><span class="pre">'clip'</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="s"><span class="pre">'none'</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="s"><span class="pre">'project'</span></span><span class="p"><span class="pre">]</span></span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">'clip'</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">n_jobs</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/quapy/method/aggregative.html#PACC"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#quapy.method.aggregative.PACC" title="Permalink to this definition"></a></dt>
|
||||
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">quapy.method.aggregative.</span></span><span class="sig-name descname"><span class="pre">PACC</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">classifier</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">BaseEstimator</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">val_split</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">5</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">solver</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Literal</span><span class="p"><span class="pre">[</span></span><span class="s"><span class="pre">'minimize'</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="s"><span class="pre">'exact'</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="s"><span class="pre">'exact-raise'</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="s"><span class="pre">'exact-cc'</span></span><span class="p"><span class="pre">]</span></span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">'minimize'</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">method</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Literal</span><span class="p"><span class="pre">[</span></span><span class="s"><span class="pre">'inversion'</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="s"><span class="pre">'invariant-ratio'</span></span><span class="p"><span class="pre">]</span></span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">'inversion'</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">norm</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Literal</span><span class="p"><span class="pre">[</span></span><span class="s"><span class="pre">'clip'</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="s"><span class="pre">'mapsimplex'</span></span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="s"><span class="pre">'condsoftmax'</span></span><span class="p"><span class="pre">]</span></span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">'clip'</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">n_jobs</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="reference internal" href="_modules/quapy/method/aggregative.html#PACC"><span class="viewcode-link"><span class="pre">[source]</span></span></a><a class="headerlink" href="#quapy.method.aggregative.PACC" title="Permalink to this definition"></a></dt>
|
||||
<dd><p>Bases: <a class="reference internal" href="#quapy.method.aggregative.AggregativeSoftQuantifier" title="quapy.method.aggregative.AggregativeSoftQuantifier"><code class="xref py py-class docutils literal notranslate"><span class="pre">AggregativeSoftQuantifier</span></code></a></p>
|
||||
<p><a class="reference external" href="https://ieeexplore.ieee.org/abstract/document/5694031">Probabilistic Adjusted Classify & Count</a>,
|
||||
the probabilistic variant of ACC that relies on the posterior probabilities returned by a probabilistic classifier.</p>
|
||||
|
@ -1062,23 +1070,31 @@ for <cite>k</cite>). Alternatively, this set can be specified at fit time by ind
|
|||
on which the predictions are to be generated.</p></li>
|
||||
<li><p><strong>method</strong> (<em>str</em>) – <p>adjustment method to be used:</p>
|
||||
<ul>
|
||||
<li><p>’inversion’: matrix inversion method based on the matrix equality <span class="math notranslate nohighlight">\(P(C)=P(C|Y)P(Y)\)</span>, which tries to invert <cite>P(C|Y)</cite> matrix.</p></li>
|
||||
<li><p>’invariant-ratio’: invariant ratio estimator of <a class="reference external" href="https://jmlr.org/papers/v20/18-456.html">Vaz et al.</a>, which replaces the last equation with the normalization condition.</p></li>
|
||||
<li><p>’inversion’: matrix inversion method based on the matrix equality <span class="math notranslate nohighlight">\(P(C)=P(C|Y)P(Y)\)</span>,
|
||||
which tries to invert <cite>P(C|Y)</cite> matrix.</p></li>
|
||||
<li><p>’invariant-ratio’: invariant ratio estimator of <a class="reference external" href="https://jmlr.org/papers/v20/18-456.html">Vaz et al.</a>,
|
||||
which replaces the last equation with the normalization condition.</p></li>
|
||||
</ul>
|
||||
</p></li>
|
||||
<li><p><strong>solver</strong> (<em>str</em>) – <p>the method to use for solving the system of linear equations. Valid options are:</p>
|
||||
<ul>
|
||||
<li><p>’exact-raise’: tries to solve the system using matrix inversion. Raises an error if the matrix has rank strictly less than <cite>n_classes</cite>.</p></li>
|
||||
<li><p>’exact-cc’: if the matrix is not of full rank, returns <cite>p_c</cite> as the estimates, which corresponds to no adjustment (i.e., the classify and count method. See <a class="reference internal" href="#quapy.method.aggregative.CC" title="quapy.method.aggregative.CC"><code class="xref py py-class docutils literal notranslate"><span class="pre">quapy.method.aggregative.CC</span></code></a>)</p></li>
|
||||
<li><p>’exact-raise’: tries to solve the system using matrix inversion.
|
||||
Raises an error if the matrix has rank strictly less than <cite>n_classes</cite>.</p></li>
|
||||
<li><p>’exact-cc’: if the matrix is not of full rank, returns <cite>p_c</cite> as the estimates, which
|
||||
corresponds to no adjustment (i.e., the classify and count method. See <a class="reference internal" href="#quapy.method.aggregative.CC" title="quapy.method.aggregative.CC"><code class="xref py py-class docutils literal notranslate"><span class="pre">quapy.method.aggregative.CC</span></code></a>)</p></li>
|
||||
<li><p>’exact’: deprecated, defaults to ‘exact-cc’</p></li>
|
||||
<li><p>’minimize’: minimizes the L2 norm of <span class="math notranslate nohighlight">\(|Ax-B|\)</span>. This one generally works better, and is the default parameter. More details about this can be consulted in <a class="reference external" href="https://lq-2022.github.io/proceedings/CompleteVolume.pdf">Bunse, M. “On Multi-Class Extensions of Adjusted Classify and Count”, on proceedings of the 2nd International Workshop on Learning to Quantify: Methods and Applications (LQ 2022), ECML/PKDD 2022, Grenoble (France)</a>.</p></li>
|
||||
<li><p>’minimize’: minimizes the L2 norm of <span class="math notranslate nohighlight">\(|Ax-B|\)</span>. This one generally works better, and is the
|
||||
default parameter. More details about this can be consulted in <a class="reference external" href="https://lq-2022.github.io/proceedings/CompleteVolume.pdf">Bunse, M. “On Multi-Class Extensions
|
||||
of Adjusted Classify and Count”, on proceedings of the 2nd International Workshop on Learning to
|
||||
Quantify: Methods and Applications (LQ 2022), ECML/PKDD 2022, Grenoble (France)</a>.</p></li>
|
||||
</ul>
|
||||
</p></li>
|
||||
<li><p><strong>clipping</strong> (<em>str</em>) – <p>the method to use for normalization.</p>
|
||||
<li><p><strong>norm</strong> (<em>str</em>) – <p>the method to use for normalization.</p>
|
||||
<ul>
|
||||
<li><p>If <cite>None</cite> or <cite>“none”</cite>, no normalization is performed.</p></li>
|
||||
<li><p>If <cite>“clip”</cite>, the values are clipped to the range [0,1] and normalized, so they sum up to 1.</p></li>
|
||||
<li><p>If <cite>“project”</cite>, the values are projected onto the probability simplex.</p></li>
|
||||
<li><p><cite>clip</cite>, the values are clipped to the range [0,1] and then L1-normalized.</p></li>
|
||||
<li><p><cite>mapsimplex</cite> projects vectors onto the probability simplex. This implementation relies on
|
||||
<a class="reference external" href="https://gist.github.com/mblondel/6f3b7aaad90606b98f71">Mathieu Blondel’s projection_simplex_sort</a></p></li>
|
||||
<li><p><cite>condsoftmax</cite>, applies a softmax normalization only to prevalence vectors that lie outside the simplex</p></li>
|
||||
</ul>
|
||||
</p></li>
|
||||
<li><p><strong>n_jobs</strong> – number of parallel workers</p></li>
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -8,36 +8,19 @@ import scipy
|
|||
import numpy as np
|
||||
|
||||
|
||||
def prevalence_linspace(grid_points:int=21, repeats:int=1, smooth_limits_epsilon:float=0.01):
|
||||
"""
|
||||
Produces an array of uniformly separated values of prevalence.
|
||||
By default, produces an array of 21 prevalence values, with
|
||||
step 0.05 and with the limits smoothed, i.e.:
|
||||
[0.01, 0.05, 0.10, 0.15, ..., 0.90, 0.95, 0.99]
|
||||
# ------------------------------------------------------------------------------------------
|
||||
# Counter utils
|
||||
# ------------------------------------------------------------------------------------------
|
||||
|
||||
:param grid_points: the number of prevalence values to sample from the [0,1] interval (default 21)
|
||||
:param repeats: number of times each prevalence is to be repeated (defaults to 1)
|
||||
:param smooth_limits_epsilon: the quantity to add and subtract to the limits 0 and 1
|
||||
:return: an array of uniformly separated prevalence values
|
||||
def counts_from_labels(labels: ArrayLike, classes: ArrayLike) -> np.ndarray:
|
||||
"""
|
||||
p = np.linspace(0., 1., num=grid_points, endpoint=True)
|
||||
p[0] += smooth_limits_epsilon
|
||||
p[-1] -= smooth_limits_epsilon
|
||||
if p[0] > p[1]:
|
||||
raise ValueError(f'the smoothing in the limits is greater than the prevalence step')
|
||||
if repeats > 1:
|
||||
p = np.repeat(p, repeats)
|
||||
return p
|
||||
|
||||
|
||||
def counts_from_labels(labels: ArrayLike, classes: ArrayLike):
|
||||
"""
|
||||
Computes the count values from a vector of labels.
|
||||
Computes the raw count values from a vector of labels.
|
||||
|
||||
:param labels: array-like of shape `(n_instances,)` with the label for each instance
|
||||
:param classes: the class labels. This is needed in order to correctly compute the prevalence vector even when
|
||||
some classes have no examples.
|
||||
:return: an ndarray of shape `(len(classes),)` with the occurrence counts of each class
|
||||
:return: ndarray of shape `(len(classes),)` with the raw counts for each class, in the same order
|
||||
as they appear in `classes`
|
||||
"""
|
||||
if np.asarray(labels).ndim != 1:
|
||||
raise ValueError(f'param labels does not seem to be a ndarray of label predictions')
|
||||
|
@ -54,10 +37,12 @@ def prevalence_from_labels(labels: ArrayLike, classes: ArrayLike):
|
|||
:param labels: array-like of shape `(n_instances,)` with the label for each instance
|
||||
:param classes: the class labels. This is needed in order to correctly compute the prevalence vector even when
|
||||
some classes have no examples.
|
||||
:return: an ndarray of shape `(len(classes))` with the class prevalence values
|
||||
:return: ndarray of shape `(len(classes),)` with the class proportions for each class, in the same order
|
||||
as they appear in `classes`
|
||||
"""
|
||||
counts = np.array(counts_from_labels(labels, classes), dtype=float)
|
||||
return counts / np.sum(counts)
|
||||
counts = counts_from_labels(labels, classes)
|
||||
prevalences = counts.astype(float) / np.sum(counts)
|
||||
return prevalences
|
||||
|
||||
|
||||
def prevalence_from_probabilities(posteriors: ArrayLike, binarize: bool = False):
|
||||
|
@ -71,7 +56,7 @@ def prevalence_from_probabilities(posteriors: ArrayLike, binarize: bool = False)
|
|||
"""
|
||||
posteriors = np.asarray(posteriors)
|
||||
if posteriors.ndim != 2:
|
||||
raise ValueError(f'param posteriors does not seem to be a ndarray of posteior probabilities')
|
||||
raise ValueError(f'param posteriors does not seem to be a ndarray of posterior probabilities')
|
||||
if binarize:
|
||||
predictions = np.argmax(posteriors, axis=-1)
|
||||
return prevalence_from_labels(predictions, np.arange(posteriors.shape[1]))
|
||||
|
@ -81,23 +66,262 @@ def prevalence_from_probabilities(posteriors: ArrayLike, binarize: bool = False)
|
|||
return prevalences
|
||||
|
||||
|
||||
def as_binary_prevalence(positive_prevalence: Union[float, np.ndarray], clip_if_necessary: bool=False):
|
||||
def num_prevalence_combinations(n_prevpoints:int, n_classes:int, n_repeats:int=1) -> int:
|
||||
"""
|
||||
Computes the number of valid prevalence combinations in the n_classes-dimensional simplex if `n_prevpoints` equally
|
||||
distant prevalence values are generated and `n_repeats` repetitions are requested.
|
||||
The computation comes down to calculating:
|
||||
|
||||
.. math::
|
||||
\\binom{N+C-1}{C-1} \\times r
|
||||
|
||||
where `N` is `n_prevpoints-1`, i.e., the number of probability mass blocks to allocate, `C` is the number of
|
||||
classes, and `r` is `n_repeats`. This solution comes from the
|
||||
`Stars and Bars <https://brilliant.org/wiki/integer-equations-star-and-bars/>`_ problem.
|
||||
|
||||
:param int n_classes: number of classes
|
||||
:param int n_prevpoints: number of prevalence points.
|
||||
:param int n_repeats: number of repetitions for each prevalence combination
|
||||
:return: The number of possible combinations. For example, if `n_classes`=2, `n_prevpoints`=5, `n_repeats`=1,
|
||||
then the number of possible combinations are 5, i.e.: [0,1], [0.25,0.75], [0.50,0.50], [0.75,0.25],
|
||||
and [1.0,0.0]
|
||||
"""
|
||||
N = n_prevpoints-1
|
||||
C = n_classes
|
||||
r = n_repeats
|
||||
return int(scipy.special.binom(N + C - 1, C - 1) * r)
|
||||
|
||||
|
||||
def get_nprevpoints_approximation(combinations_budget:int, n_classes:int, n_repeats:int=1) -> int:
|
||||
"""
|
||||
Searches for the largest number of (equidistant) prevalence points to define for each of the `n_classes` classes so
|
||||
that the number of valid prevalence values generated as combinations of prevalence points (points in a
|
||||
`n_classes`-dimensional simplex) do not exceed combinations_budget.
|
||||
|
||||
:param int combinations_budget: maximum number of combinations allowed
|
||||
:param int n_classes: number of classes
|
||||
:param int n_repeats: number of repetitions for each prevalence combination
|
||||
:return: the largest number of prevalence points that generate less than combinations_budget valid prevalences
|
||||
"""
|
||||
assert n_classes > 0 and n_repeats > 0 and combinations_budget > 0, 'parameters must be positive integers'
|
||||
n_prevpoints = 1
|
||||
while True:
|
||||
combinations = num_prevalence_combinations(n_prevpoints, n_classes, n_repeats)
|
||||
if combinations > combinations_budget:
|
||||
return n_prevpoints-1
|
||||
else:
|
||||
n_prevpoints += 1
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------------------
|
||||
# Prevalence vectors
|
||||
# ------------------------------------------------------------------------------------------
|
||||
|
||||
def as_binary_prevalence(positive_prevalence: Union[float, ArrayLike], clip_if_necessary: bool=False) -> np.ndarray:
|
||||
"""
|
||||
Helper that, given a float representing the prevalence for the positive class, returns a np.ndarray of two
|
||||
values representing a binary distribution.
|
||||
|
||||
:param positive_prevalence: prevalence for the positive class
|
||||
:param clip_if_necessary: if True, clips the value in [0,1] in order to guarantee the resulting distribution
|
||||
:param positive_prevalence: float or array-like of floats with the prevalence for the positive class
|
||||
:param bool clip_if_necessary: if True, clips the value in [0,1] in order to guarantee the resulting distribution
|
||||
is valid. If False, it then checks that the value is in the valid range, and raises an error if not.
|
||||
:return: np.ndarray of shape `(2,)`
|
||||
"""
|
||||
positive_prevalence = np.asarray(positive_prevalence, float)
|
||||
if clip_if_necessary:
|
||||
positive_prevalence = np.clip(positive_prevalence, 0, 1)
|
||||
else:
|
||||
assert 0 <= positive_prevalence <= 1, 'the value provided is not a valid prevalence for the positive class'
|
||||
assert np.logical_and(0 <= positive_prevalence, positive_prevalence <= 1).all(), \
|
||||
'the value provided is not a valid prevalence for the positive class'
|
||||
return np.asarray([1-positive_prevalence, positive_prevalence]).T
|
||||
|
||||
|
||||
def strprev(prevalences: ArrayLike, prec: int=3) -> str:
|
||||
"""
|
||||
Returns a string representation for a prevalence vector. E.g.,
|
||||
|
||||
>>> strprev([1/3, 2/3], prec=2)
|
||||
>>> '[0.33, 0.67]'
|
||||
|
||||
:param prevalences: array-like of prevalence values
|
||||
:param prec: int, indicates the float precision (number of decimal values to print)
|
||||
:return: string
|
||||
"""
|
||||
return '['+ ', '.join([f'{p:.{prec}f}' for p in prevalences]) + ']'
|
||||
|
||||
|
||||
def check_prevalence_vector(prevalences: ArrayLike, raise_exception: bool=False, tolerance: float=1e-08, aggr=True):
|
||||
"""
|
||||
Checks that `prevalences` is a valid prevalence vector, i.e., it contains values in [0,1] and
|
||||
the values sum up to 1. In other words, verifies that the `prevalences` vectors lies in the
|
||||
probability simplex.
|
||||
|
||||
:param ArrayLike prevalences: the prevalence vector, or vectors, to check
|
||||
:param bool raise_exception: whether to raise an exception if the vector (or any of the vectors) does
|
||||
not lie in the simplex (default False)
|
||||
:param float tolerance: error tolerance for the check `sum(prevalences) - 1 = 0`
|
||||
:param bool aggr: if True (default) returns one single bool (True if all prevalence vectors are valid,
|
||||
False otherwise), if False returns an array of bool, one for each prevalence vector
|
||||
:return: a single bool True if `prevalences` is a vector of prevalence values that lies on the simplex,
|
||||
or False otherwise; alternatively, if `prevalences` is a matrix of shape `(num_vectors, n_classes,)`
|
||||
then it returns one such bool for each prevalence vector
|
||||
"""
|
||||
prevalences = np.asarray(prevalences)
|
||||
|
||||
all_positive = prevalences>=0
|
||||
if not all_positive.all():
|
||||
if raise_exception:
|
||||
raise ValueError('some prevalence vectors contain negative numbers; '
|
||||
'consider using the qp.functional.normalize_prevalence with '
|
||||
'any method from ["clip", "mapsimplex", "softmax"]')
|
||||
|
||||
all_close_1 = np.isclose(prevalences.sum(axis=-1), 1, atol=tolerance)
|
||||
if not all_close_1.all():
|
||||
if raise_exception:
|
||||
raise ValueError('some prevalence vectors do not sum up to 1; '
|
||||
'consider using the qp.functional.normalize_prevalence with '
|
||||
'any method from ["l1", "clip", "mapsimplex", "softmax"]')
|
||||
|
||||
valid = np.logical_and(all_positive.all(axis=-1), all_close_1)
|
||||
if aggr:
|
||||
return valid.all()
|
||||
else:
|
||||
return valid
|
||||
|
||||
|
||||
def normalize_prevalence(prevalences: ArrayLike, method='l1'):
|
||||
"""
|
||||
Normalizes a vector or matrix of prevalence values. The normalization consists of applying a L1 normalization in
|
||||
cases in which the prevalence values are not all-zeros, and to convert the prevalence values into `1/n_classes` in
|
||||
cases in which all values are zero.
|
||||
|
||||
:param prevalences: array-like of shape `(n_classes,)` or of shape `(n_samples, n_classes,)` with prevalence values
|
||||
:param str method: indicates the normalization method to employ, options are:
|
||||
|
||||
* `l1`: applies L1 normalization (default); a 0 vector is mapped onto the uniform prevalence
|
||||
* `clip`: clip values in [0,1] and then rescales so that the L1 norm is 1
|
||||
* `mapsimplex`: projects vectors onto the probability simplex. This implementation relies on
|
||||
`Mathieu Blondel's projection_simplex_sort <https://gist.github.com/mblondel/6f3b7aaad90606b98f71>`_
|
||||
* `softmax`: applies softmax to all vectors
|
||||
* `condsoftmax`: applies softmax only to invalid prevalence vectors
|
||||
|
||||
:return: a normalized vector or matrix of prevalence values
|
||||
"""
|
||||
if method in ['none', None]:
|
||||
return prevalences
|
||||
|
||||
prevalences = np.asarray(prevalences, dtype=float)
|
||||
|
||||
if method=='l1':
|
||||
normalized = l1_norm(prevalences)
|
||||
check_prevalence_vector(normalized, raise_exception=True)
|
||||
elif method=='clip':
|
||||
normalized = clip(prevalences) # no need to check afterwards
|
||||
elif method=='mapsimplex':
|
||||
normalized = projection_simplex_sort(prevalences)
|
||||
elif method=='softmax':
|
||||
normalized = softmax(prevalences)
|
||||
elif method=='condsoftmax':
|
||||
normalized = condsoftmax(prevalences)
|
||||
else:
|
||||
raise ValueError(f'unknown {method=}, valid ones are ["l1", "clip", "mapsimplex", "softmax"]')
|
||||
|
||||
return normalized
|
||||
|
||||
|
||||
def l1_norm(prevalences: ArrayLike) -> np.ndarray:
|
||||
"""
|
||||
Applies L1 normalization to the `unnormalized_arr` so that it becomes a valid prevalence
|
||||
vector. Zero vectors are mapped onto the uniform distribution. Raises an exception if
|
||||
the resulting vectors are not valid distributions. This may happen when the original
|
||||
prevalence vectors contain negative values. Use the `clip` normalization function
|
||||
instead to avoid this possibility.
|
||||
|
||||
:param prevalences: array-like of shape `(n_classes,)` or of shape `(n_samples, n_classes,)` with prevalence values
|
||||
:return: np.ndarray representing a valid distribution
|
||||
"""
|
||||
n_classes = prevalences.shape[-1]
|
||||
accum = prevalences.sum(axis=-1, keepdims=True)
|
||||
prevalences = np.true_divide(prevalences, accum, where=accum > 0)
|
||||
allzeros = accum.flatten() == 0
|
||||
if any(allzeros):
|
||||
if prevalences.ndim == 1:
|
||||
prevalences = np.full(shape=n_classes, fill_value=1. / n_classes)
|
||||
else:
|
||||
prevalences[allzeros] = np.full(shape=n_classes, fill_value=1. / n_classes)
|
||||
return prevalences
|
||||
|
||||
|
||||
def clip(prevalences: ArrayLike) -> np.ndarray:
|
||||
"""
|
||||
Clips the values in [0,1] and then applies the L1 normalization.
|
||||
|
||||
:param prevalences: array-like of shape `(n_classes,)` or of shape `(n_samples, n_classes,)` with prevalence values
|
||||
:return: np.ndarray representing a valid distribution
|
||||
"""
|
||||
clipped = np.clip(prevalences, 0, 1)
|
||||
normalized = l1_norm(clipped)
|
||||
return normalized
|
||||
|
||||
|
||||
def projection_simplex_sort(unnormalized_arr: ArrayLike) -> np.ndarray:
|
||||
"""Projects a point onto the probability simplex.
|
||||
|
||||
The code is adapted from Mathieu Blondel's BSD-licensed
|
||||
`implementation <https://gist.github.com/mblondel/6f3b7aaad90606b98f71>`_
|
||||
(see function `projection_simplex_sort` in their repo) which is accompanying the paper
|
||||
|
||||
Mathieu Blondel, Akinori Fujino, and Naonori Ueda.
|
||||
Large-scale Multiclass Support Vector Machine Training via Euclidean Projection onto the Simplex,
|
||||
ICPR 2014, `URL <http://www.mblondel.org/publications/mblondel-icpr2014.pdf>`_
|
||||
|
||||
:param `unnormalized_arr`: point in n-dimensional space, shape `(n,)`
|
||||
:return: projection of `unnormalized_arr` onto the (n-1)-dimensional probability simplex, shape `(n,)`
|
||||
"""
|
||||
unnormalized_arr = np.asarray(unnormalized_arr)
|
||||
n = len(unnormalized_arr)
|
||||
u = np.sort(unnormalized_arr)[::-1]
|
||||
cssv = np.cumsum(u) - 1.0
|
||||
ind = np.arange(1, n + 1)
|
||||
cond = u - cssv / ind > 0
|
||||
rho = ind[cond][-1]
|
||||
theta = cssv[cond][-1] / float(rho)
|
||||
return np.maximum(unnormalized_arr - theta, 0)
|
||||
|
||||
|
||||
def softmax(prevalences: ArrayLike) -> np.ndarray:
|
||||
"""
|
||||
Applies the softmax function to all vectors even if the original vectors were valid distributions.
|
||||
If you want to leave valid vectors untouched, use condsoftmax instead.
|
||||
|
||||
:param prevalences: array-like of shape `(n_classes,)` or of shape `(n_samples, n_classes,)` with prevalence values
|
||||
:return: np.ndarray representing a valid distribution
|
||||
"""
|
||||
normalized = scipy.special.softmax(prevalences, axis=-1)
|
||||
return normalized
|
||||
|
||||
|
||||
def condsoftmax(prevalences: ArrayLike) -> np.ndarray:
|
||||
"""
|
||||
Applies the softmax function only to vectors that do not represent valid distributions.
|
||||
|
||||
:param prevalences: array-like of shape `(n_classes,)` or of shape `(n_samples, n_classes,)` with prevalence values
|
||||
:return: np.ndarray representing a valid distribution
|
||||
"""
|
||||
invalid_idx = ~ check_prevalence_vector(prevalences, aggr=False, raise_exception=False)
|
||||
if isinstance(invalid_idx, np.bool_) and invalid_idx:
|
||||
# only one vector
|
||||
normalized = scipy.special.softmax(prevalences)
|
||||
else:
|
||||
prevalences = np.copy(prevalences)
|
||||
prevalences[invalid_idx] = scipy.special.softmax(prevalences[invalid_idx], axis=-1)
|
||||
normalized = prevalences
|
||||
return normalized
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------------------
|
||||
# Divergences
|
||||
# ------------------------------------------------------------------------------------------
|
||||
|
||||
def HellingerDistance(P: np.ndarray, Q: np.ndarray) -> float:
|
||||
"""
|
||||
|
@ -128,188 +352,6 @@ def TopsoeDistance(P: np.ndarray, Q: np.ndarray, epsilon: float=1e-20):
|
|||
:return: float
|
||||
"""
|
||||
return np.sum(P*np.log((2*P+epsilon)/(P+Q+epsilon)) + Q*np.log((2*Q+epsilon)/(P+Q+epsilon)))
|
||||
|
||||
|
||||
def uniform_prevalence_sampling(n_classes: int, size: int=1):
|
||||
"""
|
||||
Implements the `Kraemer algorithm <http://www.cs.cmu.edu/~nasmith/papers/smith+tromble.tr04.pdf>`_
|
||||
for sampling uniformly at random from the unit simplex. This implementation is adapted from this
|
||||
`post <https://cs.stackexchange.com/questions/3227/uniform-sampling-from-a-simplex>_`.
|
||||
|
||||
:param n_classes: integer, number of classes (dimensionality of the simplex)
|
||||
:param size: number of samples to return
|
||||
:return: `np.ndarray` of shape `(size, n_classes,)` if `size>1`, or of shape `(n_classes,)` otherwise
|
||||
"""
|
||||
if n_classes == 2:
|
||||
u = np.random.rand(size)
|
||||
u = np.vstack([1-u, u]).T
|
||||
else:
|
||||
u = np.random.rand(size, n_classes-1)
|
||||
u.sort(axis=-1)
|
||||
_0s = np.zeros(shape=(size, 1))
|
||||
_1s = np.ones(shape=(size, 1))
|
||||
a = np.hstack([_0s, u])
|
||||
b = np.hstack([u, _1s])
|
||||
u = b-a
|
||||
if size == 1:
|
||||
u = u.flatten()
|
||||
return u
|
||||
|
||||
|
||||
uniform_simplex_sampling = uniform_prevalence_sampling
|
||||
|
||||
|
||||
def strprev(prevalences: ArrayLike, prec: int=3):
|
||||
"""
|
||||
Returns a string representation for a prevalence vector. E.g.,
|
||||
|
||||
>>> strprev([1/3, 2/3], prec=2)
|
||||
>>> '[0.33, 0.67]'
|
||||
|
||||
:param prevalences: a vector of prevalence values
|
||||
:param prec: float precision
|
||||
:return: string
|
||||
"""
|
||||
return '['+ ', '.join([f'{p:.{prec}f}' for p in prevalences]) + ']'
|
||||
|
||||
|
||||
def adjusted_quantification(prevalence_estim: ArrayLike, tpr: float, fpr: float, clip: bool=True):
|
||||
"""
|
||||
Implements the adjustment of ACC and PACC for the binary case. The adjustment for a prevalence estimate of the
|
||||
positive class `p` comes down to computing:
|
||||
|
||||
.. math::
|
||||
ACC(p) = \\frac{ p - fpr }{ tpr - fpr }
|
||||
|
||||
:param prevalence_estim: float, the estimated value for the positive class
|
||||
:param tpr: float, the true positive rate of the classifier
|
||||
:param fpr: float, the false positive rate of the classifier
|
||||
:param clip: set to True (default) to clip values that might exceed the range [0,1]
|
||||
:return: float, the adjusted count
|
||||
"""
|
||||
|
||||
den = tpr - fpr
|
||||
if den == 0:
|
||||
den += 1e-8
|
||||
adjusted = (prevalence_estim - fpr) / den
|
||||
if clip:
|
||||
adjusted = np.clip(adjusted, 0., 1.)
|
||||
return adjusted
|
||||
|
||||
|
||||
def normalize_prevalence(prevalences: ArrayLike):
|
||||
"""
|
||||
Normalize a vector or matrix of prevalence values. The normalization consists of applying a L1 normalization in
|
||||
cases in which the prevalence values are not all-zeros, and to convert the prevalence values into `1/n_classes` in
|
||||
cases in which all values are zero.
|
||||
|
||||
:param prevalences: array-like of shape `(n_classes,)` or of shape `(n_samples, n_classes,)` with prevalence values
|
||||
:return: a normalized vector or matrix of prevalence values
|
||||
"""
|
||||
prevalences = np.asarray(prevalences)
|
||||
n_classes = prevalences.shape[-1]
|
||||
accum = prevalences.sum(axis=-1, keepdims=True)
|
||||
prevalences = np.true_divide(prevalences, accum, where=accum>0)
|
||||
allzeros = accum.flatten()==0
|
||||
if any(allzeros):
|
||||
if prevalences.ndim == 1:
|
||||
prevalences = np.full(shape=n_classes, fill_value=1./n_classes)
|
||||
else:
|
||||
prevalences[accum.flatten()==0] = np.full(shape=n_classes, fill_value=1./n_classes)
|
||||
return prevalences
|
||||
|
||||
|
||||
def __num_prevalence_combinations_depr(n_prevpoints:int, n_classes:int, n_repeats:int=1):
|
||||
"""
|
||||
Computes the number of prevalence combinations in the n_classes-dimensional simplex if `nprevpoints` equally distant
|
||||
prevalence values are generated and `n_repeats` repetitions are requested.
|
||||
|
||||
:param n_classes: integer, number of classes
|
||||
:param n_prevpoints: integer, number of prevalence points.
|
||||
:param n_repeats: integer, number of repetitions for each prevalence combination
|
||||
:return: The number of possible combinations. For example, if n_classes=2, n_prevpoints=5, n_repeats=1, then the
|
||||
number of possible combinations are 5, i.e.: [0,1], [0.25,0.75], [0.50,0.50], [0.75,0.25], and [1.0,0.0]
|
||||
"""
|
||||
__cache={}
|
||||
def __f(nc,np):
|
||||
if (nc,np) in __cache: # cached result
|
||||
return __cache[(nc,np)]
|
||||
if nc==1: # stop condition
|
||||
return 1
|
||||
else: # recursive call
|
||||
x = sum([__f(nc-1, np-i) for i in range(np)])
|
||||
__cache[(nc,np)] = x
|
||||
return x
|
||||
return __f(n_classes, n_prevpoints) * n_repeats
|
||||
|
||||
|
||||
def num_prevalence_combinations(n_prevpoints:int, n_classes:int, n_repeats:int=1):
|
||||
"""
|
||||
Computes the number of valid prevalence combinations in the n_classes-dimensional simplex if `n_prevpoints` equally
|
||||
distant prevalence values are generated and `n_repeats` repetitions are requested.
|
||||
The computation comes down to calculating:
|
||||
|
||||
.. math::
|
||||
\\binom{N+C-1}{C-1} \\times r
|
||||
|
||||
where `N` is `n_prevpoints-1`, i.e., the number of probability mass blocks to allocate, `C` is the number of
|
||||
classes, and `r` is `n_repeats`. This solution comes from the
|
||||
`Stars and Bars <https://brilliant.org/wiki/integer-equations-star-and-bars/>`_ problem.
|
||||
|
||||
:param n_classes: integer, number of classes
|
||||
:param n_prevpoints: integer, number of prevalence points.
|
||||
:param n_repeats: integer, number of repetitions for each prevalence combination
|
||||
:return: The number of possible combinations. For example, if n_classes=2, n_prevpoints=5, n_repeats=1, then the
|
||||
number of possible combinations are 5, i.e.: [0,1], [0.25,0.75], [0.50,0.50], [0.75,0.25], and [1.0,0.0]
|
||||
"""
|
||||
N = n_prevpoints-1
|
||||
C = n_classes
|
||||
r = n_repeats
|
||||
return int(scipy.special.binom(N + C - 1, C - 1) * r)
|
||||
|
||||
|
||||
def get_nprevpoints_approximation(combinations_budget:int, n_classes:int, n_repeats:int=1):
|
||||
"""
|
||||
Searches for the largest number of (equidistant) prevalence points to define for each of the `n_classes` classes so
|
||||
that the number of valid prevalence values generated as combinations of prevalence points (points in a
|
||||
`n_classes`-dimensional simplex) do not exceed combinations_budget.
|
||||
|
||||
:param combinations_budget: integer, maximum number of combinations allowed
|
||||
:param n_classes: integer, number of classes
|
||||
:param n_repeats: integer, number of repetitions for each prevalence combination
|
||||
:return: the largest number of prevalence points that generate less than combinations_budget valid prevalences
|
||||
"""
|
||||
assert n_classes > 0 and n_repeats > 0 and combinations_budget > 0, 'parameters must be positive integers'
|
||||
n_prevpoints = 1
|
||||
while True:
|
||||
combinations = num_prevalence_combinations(n_prevpoints, n_classes, n_repeats)
|
||||
if combinations > combinations_budget:
|
||||
return n_prevpoints-1
|
||||
else:
|
||||
n_prevpoints += 1
|
||||
|
||||
|
||||
def check_prevalence_vector(prevalences: ArrayLike, raise_exception: bool=False, toleranze: float=1e-08):
|
||||
"""
|
||||
Checks that p is a valid prevalence vector, i.e., that it contains values in [0,1] and that the values sum up to 1.
|
||||
|
||||
:param prevalences: the prevalence vector to check
|
||||
:return: True if `p` is valid, False otherwise
|
||||
"""
|
||||
prevalences = np.asarray(prevalences)
|
||||
if not all(prevalences >= 0):
|
||||
if raise_exception:
|
||||
raise ValueError('the prevalence vector contains negative numbers')
|
||||
return False
|
||||
if not all(prevalences <= 1):
|
||||
if raise_exception:
|
||||
raise ValueError('the prevalence vector contains values >1')
|
||||
return False
|
||||
if not np.isclose(prevalences.sum(), 1, atol=toleranze):
|
||||
if raise_exception:
|
||||
raise ValueError('the prevalence vector does not sum up to 1')
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def get_divergence(divergence: Union[str, Callable]):
|
||||
|
@ -334,6 +376,10 @@ def get_divergence(divergence: Union[str, Callable]):
|
|||
raise ValueError(f'argument "divergence" not understood; use a str or a callable function')
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------------------
|
||||
# Solvers
|
||||
# ------------------------------------------------------------------------------------------
|
||||
|
||||
def argmin_prevalence(loss: Callable,
|
||||
n_classes: int,
|
||||
method: Literal["optim_minimize", "linear_search", "ternary_search"]='optim_minimize'):
|
||||
|
@ -353,7 +399,7 @@ def argmin_prevalence(loss: Callable,
|
|||
elif method == 'linear_search':
|
||||
return linear_search(loss, n_classes)
|
||||
elif method == 'ternary_search':
|
||||
raise NotImplementedError()
|
||||
ternary_search(loss, n_classes)
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
|
||||
|
@ -401,94 +447,135 @@ def linear_search(loss: Callable, n_classes: int):
|
|||
return np.asarray([1 - prev_selected, prev_selected])
|
||||
|
||||
|
||||
def map_onto_probability_simplex(unnormalized_arr: ArrayLike) -> np.ndarray:
|
||||
"""Projects a point onto the probability simplex.
|
||||
def ternary_search(loss: Callable, n_classes: int):
|
||||
raise NotImplementedError()
|
||||
|
||||
The code is adapted from Mathieu Blondel's BSD-licensed
|
||||
`implementation <https://gist.github.com/mblondel/6f3b7aaad90606b98f71>`_
|
||||
which is accompanying the paper
|
||||
|
||||
Mathieu Blondel, Akinori Fujino, and Naonori Ueda.
|
||||
Large-scale Multiclass Support Vector Machine Training via Euclidean Projection onto the Simplex,
|
||||
ICPR 2014, `URL <http://www.mblondel.org/publications/mblondel-icpr2014.pdf>`_
|
||||
# ------------------------------------------------------------------------------------------
|
||||
# Sampling utils
|
||||
# ------------------------------------------------------------------------------------------
|
||||
|
||||
:param unnormalized_arr: point in n-dimensional space, shape `(n,)`
|
||||
:return: projection of `v` onto (n-1)-dimensional probability simplex, shape `(n,)`
|
||||
def prevalence_linspace(grid_points:int=21, repeats:int=1, smooth_limits_epsilon:float=0.01) -> np.ndarray:
|
||||
"""
|
||||
unnormalized_arr = np.asarray(unnormalized_arr)
|
||||
n = len(unnormalized_arr)
|
||||
Produces an array of uniformly separated values of prevalence.
|
||||
By default, produces an array of 21 prevalence values, with
|
||||
step 0.05 and with the limits smoothed, i.e.:
|
||||
[0.01, 0.05, 0.10, 0.15, ..., 0.90, 0.95, 0.99]
|
||||
|
||||
# Sort the values in the descending order
|
||||
u = np.sort(unnormalized_arr)[::-1]
|
||||
|
||||
cssv = np.cumsum(u) - 1.0
|
||||
ind = np.arange(1, n + 1)
|
||||
cond = u - cssv / ind > 0
|
||||
rho = ind[cond][-1]
|
||||
theta = cssv[cond][-1] / float(rho)
|
||||
return np.maximum(unnormalized_arr - theta, 0)
|
||||
|
||||
|
||||
def clip_prevalence(prevalences: ArrayLike, method: Literal[None, "none", "clip", "project"]) -> np.ndarray:
|
||||
:param grid_points: the number of prevalence values to sample from the [0,1] interval (default 21)
|
||||
:param repeats: number of times each prevalence is to be repeated (defaults to 1)
|
||||
:param smooth_limits_epsilon: the quantity to add and subtract to the limits 0 and 1
|
||||
:return: an array of uniformly separated prevalence values
|
||||
"""
|
||||
Clips the proportions vector `prevalences` so that it is a valid probability distribution, i.e., all values
|
||||
are in [0,1] and sum up to 1.
|
||||
p = np.linspace(0., 1., num=grid_points, endpoint=True)
|
||||
p[0] += smooth_limits_epsilon
|
||||
p[-1] -= smooth_limits_epsilon
|
||||
if p[0] > p[1]:
|
||||
raise ValueError(f'the smoothing in the limits is greater than the prevalence step')
|
||||
if repeats > 1:
|
||||
p = np.repeat(p, repeats)
|
||||
return p
|
||||
|
||||
:param prevalences: array-like, the proportions vector to be clipped, shape `(n_classes,)`
|
||||
:param method: indicates the method to be used for normalization.
|
||||
If `None` or `"none"`, no normalization is performed.
|
||||
If `"clip"`, the values are clipped to the range [0,1] and normalized, so they sum up to 1.
|
||||
If `"project"`, the values are projected onto the probability simplex.
|
||||
:return: the normalized prevalence vector, shape `(n_classes,)`
|
||||
|
||||
def uniform_prevalence_sampling(n_classes: int, size: int=1) -> np.ndarray:
|
||||
"""
|
||||
prevalences = np.asarray(prevalences)
|
||||
if method in [None, "none"]:
|
||||
return prevalences
|
||||
elif method == "clip":
|
||||
clipped = np.clip(prevalences, 0, 1)
|
||||
adjusted = clipped / clipped.sum()
|
||||
return adjusted
|
||||
elif method == "project":
|
||||
return map_onto_probability_simplex(prevalences)
|
||||
Implements the `Kraemer algorithm <http://www.cs.cmu.edu/~nasmith/papers/smith+tromble.tr04.pdf>`_
|
||||
for sampling uniformly at random from the unit simplex. This implementation is adapted from this
|
||||
`post <https://cs.stackexchange.com/questions/3227/uniform-sampling-from-a-simplex>_`.
|
||||
|
||||
:param n_classes: integer, number of classes (dimensionality of the simplex)
|
||||
:param size: number of samples to return
|
||||
:return: `np.ndarray` of shape `(size, n_classes,)` if `size>1`, or of shape `(n_classes,)` otherwise
|
||||
"""
|
||||
if n_classes == 2:
|
||||
u = np.random.rand(size)
|
||||
u = np.vstack([1-u, u]).T
|
||||
else:
|
||||
raise ValueError(f'Unknown method {method}. Valid ones are "none", "clip", or "project"')
|
||||
u = np.random.rand(size, n_classes-1)
|
||||
u.sort(axis=-1)
|
||||
_0s = np.zeros(shape=(size, 1))
|
||||
_1s = np.ones(shape=(size, 1))
|
||||
a = np.hstack([_0s, u])
|
||||
b = np.hstack([u, _1s])
|
||||
u = b-a
|
||||
if size == 1:
|
||||
u = u.flatten()
|
||||
return u
|
||||
|
||||
|
||||
uniform_simplex_sampling = uniform_prevalence_sampling
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------------------
|
||||
# Adjustment
|
||||
# ------------------------------------------------------------------------------------------
|
||||
|
||||
def solve_adjustment_binary(prevalence_estim: ArrayLike, tpr: float, fpr: float, clip: bool=True):
|
||||
"""
|
||||
Implements the adjustment of ACC and PACC for the binary case. The adjustment for a prevalence estimate of the
|
||||
positive class `p` comes down to computing:
|
||||
|
||||
.. math::
|
||||
ACC(p) = \\frac{ p - fpr }{ tpr - fpr }
|
||||
|
||||
:param float prevalence_estim: the estimated value for the positive class (`p` in the formula)
|
||||
:param float tpr: the true positive rate of the classifier
|
||||
:param float fpr: the false positive rate of the classifier
|
||||
:param bool clip: set to True (default) to clip values that might exceed the range [0,1]
|
||||
:return: float, the adjusted count
|
||||
"""
|
||||
|
||||
den = tpr - fpr
|
||||
if den == 0:
|
||||
den += 1e-8
|
||||
adjusted = (prevalence_estim - fpr) / den
|
||||
if clip:
|
||||
adjusted = np.clip(adjusted, 0., 1.)
|
||||
return adjusted
|
||||
|
||||
|
||||
def solve_adjustment(
|
||||
p_c_cond_y: np.ndarray,
|
||||
p_c: np.ndarray,
|
||||
class_conditional_rates: np.ndarray,
|
||||
unadjusted_counts: np.ndarray,
|
||||
method: Literal["inversion", "invariant-ratio"],
|
||||
solver: Literal["exact", "minimize", "exact-raise", "exact-cc"]) -> np.ndarray:
|
||||
"""
|
||||
Function that tries to solve for the equation :math:`P(C)=P(C|Y)P(Y)`, where :math:`P(C)` is the vector of
|
||||
prevalence values obtained by a classify and count, and :math:`P(C|Y)` are the class-conditional misclassification
|
||||
rates of the classifier.
|
||||
Function that tries to solve for :math:`p` the equation :math:`q = M p`, where :math:`q` is the vector of
|
||||
`unadjusted counts` (as estimated, e.g., via classify and count) with :math:`q_i` an estimate of
|
||||
:math:`P(\hat{Y}=y_i)`, and where :math:`M` is the matrix of `class-conditional rates` with :math:`M_{ij}` an
|
||||
estimate of :math:`P(\hat{Y}=y_i|Y=y_j)`.
|
||||
|
||||
:param p_c_cond_y: array of shape `(n_classes, n_classes,)` with entry `(c,y)` being the estimate
|
||||
of :math:`P(C=c|Y=y)`, that is, the probability that an instance that belongs to class :math:`y`
|
||||
ends up being classified as belonging to class :math:`c`
|
||||
:param class_conditional_rates: array of shape `(n_classes, n_classes,)` with entry `(i,j)` being the estimate
|
||||
of :math:`P(\hat{Y}=y_i|Y=y_j)`, that is, the probability that an instance that belongs to class :math:`y_j`
|
||||
ends up being classified as belonging to class :math:`y_i`
|
||||
|
||||
:param p_c: array of shape `(n_classes,)` containing the prevalence values as estimated by classify and count
|
||||
:param unadjusted_counts: array of shape `(n_classes,)` containing the unadjusted prevalence values (e.g., as
|
||||
estimated by CC or PCC)
|
||||
|
||||
:param str method: indicates the adjustment method to be used. Valid options are:
|
||||
|
||||
* 'inversion': tries to solve the equation :math:`P(C)=P(C|Y)P(Y)` as :math:`P(Y) = P(C|Y)^{-1} P(C)` where :math:`P(C|Y)^{-1}` is the matrix inversion of :math:`P(C|Y)`. This inversion may not exist in degenerated cases
|
||||
* 'invariant-ratio': invariant ratio estimator of `Vaz et al. 2018 <https://jmlr.org/papers/v20/18-456.html>`_, which replaces the last equation with the normalization condition.
|
||||
* `inversion`: tries to solve the equation :math:`q = M p` as :math:`p = M^{-1} q` where
|
||||
:math:`M^{-1}` is the matrix inversion of :math:`M`. This inversion may not exist in
|
||||
degenerated cases.
|
||||
* `invariant-ratio`: invariant ratio estimator of `Vaz et al. 2018 <https://jmlr.org/papers/v20/18-456.html>`_,
|
||||
which replaces the last equation in :math:`M` with the normalization condition (i.e., that the sum of
|
||||
all prevalence values must equal 1).
|
||||
|
||||
:param str solver: the method to use for solving the system of linear equations. Valid options are:
|
||||
|
||||
* 'exact-raise': tries to solve the system using matrix inversion. Raises an error if the matrix has rank strictly less than `n_classes`.
|
||||
* 'exact-cc': if the matrix is not of full rank, returns `p_c` as the estimates, which corresponds to no adjustment (i.e., the classify and count method. See :class:`quapy.method.aggregative.CC`)
|
||||
* 'exact': deprecated, defaults to 'exact-cc'
|
||||
* 'minimize': minimizes a loss, so the solution always exists
|
||||
* `exact-raise`: tries to solve the system using matrix inversion. Raises an error if the matrix has rank
|
||||
strictly lower than `n_classes`.
|
||||
* `exact-cc`: if the matrix is not full rank, returns :math:`q` (i.e., the unadjusted counts) as the estimates
|
||||
* `exact`: deprecated, defaults to 'exact-cc' (will be removed in future versions)
|
||||
* `minimize`: minimizes a loss, so the solution always exists
|
||||
"""
|
||||
if solver == "exact":
|
||||
warnings.warn(
|
||||
"The 'exact' solver is deprecated. Use 'exact-raise' or 'exact-cc'", DeprecationWarning, stacklevel=2)
|
||||
solver = "exact-cc"
|
||||
|
||||
A = np.asarray(p_c_cond_y, dtype=float)
|
||||
B = np.asarray(p_c, dtype=float)
|
||||
A = np.asarray(class_conditional_rates, dtype=float)
|
||||
B = np.asarray(unadjusted_counts, dtype=float)
|
||||
|
||||
if method == "inversion":
|
||||
pass # We leave A and B unchanged
|
||||
|
@ -497,13 +584,13 @@ def solve_adjustment(
|
|||
A[-1, :] = 1.0
|
||||
B[-1] = 1.0
|
||||
else:
|
||||
raise ValueError(f"Method {method} not known.")
|
||||
raise ValueError(f"unknown {method=}")
|
||||
|
||||
if solver == "minimize":
|
||||
def loss(prev):
|
||||
return np.linalg.norm(A @ prev - B)
|
||||
return optim_minimize(loss, n_classes=A.shape[0])
|
||||
else:
|
||||
elif solver in ["exact-raise", "exact-cc"]:
|
||||
# Solvers based on matrix inversion, so we use try/except block
|
||||
try:
|
||||
return np.linalg.solve(A, B)
|
||||
|
@ -514,6 +601,8 @@ def solve_adjustment(
|
|||
if solver == "exact-raise":
|
||||
raise
|
||||
elif solver == "exact-cc":
|
||||
return p_c
|
||||
return unadjusted_counts
|
||||
else:
|
||||
raise ValueError(f"Solver {solver} not known.")
|
||||
else:
|
||||
raise ValueError(f'unknown {solver=}')
|
||||
|
|
|
@ -394,21 +394,30 @@ class ACC(AggregativeCrispQuantifier):
|
|||
|
||||
:param str method: adjustment method to be used:
|
||||
|
||||
* 'inversion': matrix inversion method based on the matrix equality :math:`P(C)=P(C|Y)P(Y)`, which tries to invert :math:`P(C|Y)` matrix.
|
||||
* 'invariant-ratio': invariant ratio estimator of `Vaz et al. 2018 <https://jmlr.org/papers/v20/18-456.html>`_, which replaces the last equation with the normalization condition.
|
||||
* 'inversion': matrix inversion method based on the matrix equality :math:`P(C)=P(C|Y)P(Y)`,
|
||||
which tries to invert :math:`P(C|Y)` matrix.
|
||||
* 'invariant-ratio': invariant ratio estimator of `Vaz et al. 2018 <https://jmlr.org/papers/v20/18-456.html>`_,
|
||||
which replaces the last equation with the normalization condition.
|
||||
|
||||
:param str solver: indicates the method to use for solving the system of linear equations. Valid options are:
|
||||
|
||||
* 'exact-raise': tries to solve the system using matrix inversion. Raises an error if the matrix has rank strictly less than `n_classes`.
|
||||
* 'exact-cc': if the matrix is not of full rank, returns `p_c` as the estimates, which corresponds to no adjustment (i.e., the classify and count method. See :class:`quapy.method.aggregative.CC`)
|
||||
* 'exact-raise': tries to solve the system using matrix inversion. Raises an error if the matrix has rank
|
||||
strictly less than `n_classes`.
|
||||
* 'exact-cc': if the matrix is not of full rank, returns `p_c` as the estimates, which corresponds to
|
||||
no adjustment (i.e., the classify and count method. See :class:`quapy.method.aggregative.CC`)
|
||||
* 'exact': deprecated, defaults to 'exact-cc'
|
||||
* 'minimize': minimizes the L2 norm of :math:`|Ax-B|`. This one generally works better, and is the default parameter. More details about this can be consulted in `Bunse, M. "On Multi-Class Extensions of Adjusted Classify and Count", on proceedings of the 2nd International Workshop on Learning to Quantify: Methods and Applications (LQ 2022), ECML/PKDD 2022, Grenoble (France) <https://lq-2022.github.io/proceedings/CompleteVolume.pdf>`_.
|
||||
* 'minimize': minimizes the L2 norm of :math:`|Ax-B|`. This one generally works better, and is the
|
||||
default parameter. More details about this can be consulted in `Bunse, M. "On Multi-Class Extensions of
|
||||
Adjusted Classify and Count", on proceedings of the 2nd International Workshop on Learning to Quantify:
|
||||
Methods and Applications (LQ 2022), ECML/PKDD 2022, Grenoble (France)
|
||||
<https://lq-2022.github.io/proceedings/CompleteVolume.pdf>`_.
|
||||
|
||||
:param str clipping: the method to use for normalization.
|
||||
:param str norm: the method to use for normalization.
|
||||
|
||||
* If `None` or `"none"`, no normalization is performed.
|
||||
* If `"clip"`, the values are clipped to the range [0,1] and normalized, so they sum up to 1.
|
||||
* If `"project"`, the values are projected onto the probability simplex.
|
||||
* `clip`, the values are clipped to the range [0,1] and then L1-normalized.
|
||||
* `mapsimplex` projects vectors onto the probability simplex. This implementation relies on
|
||||
`Mathieu Blondel's projection_simplex_sort <https://gist.github.com/mblondel/6f3b7aaad90606b98f71>`_
|
||||
* `condsoftmax`, applies a softmax normalization only to prevalence vectors that lie outside the simplex
|
||||
|
||||
:param n_jobs: number of parallel workers
|
||||
"""
|
||||
|
@ -418,26 +427,25 @@ class ACC(AggregativeCrispQuantifier):
|
|||
val_split=5,
|
||||
solver: Literal['minimize', 'exact', 'exact-raise', 'exact-cc'] = 'minimize',
|
||||
method: Literal['inversion', 'invariant-ratio'] = 'inversion',
|
||||
clipping: Literal['clip', 'none', 'project'] = 'clip',
|
||||
norm: Literal['clip', 'mapsimplex', 'condsoftmax'] = 'clip',
|
||||
n_jobs=None,
|
||||
):
|
||||
):
|
||||
self.classifier = classifier
|
||||
self.val_split = val_split
|
||||
self.n_jobs = qp._get_njobs(n_jobs)
|
||||
self.solver = solver
|
||||
self.method = method
|
||||
self.clipping = clipping
|
||||
self.norm = norm
|
||||
|
||||
SOLVERS = ['exact', 'minimize', 'exact-raise', 'exact-cc']
|
||||
METHODS = ['inversion', 'invariant-ratio']
|
||||
CLIPPING = ['clip', 'none', 'project', None]
|
||||
|
||||
NORMALIZATIONS = ['clip', 'mapsimplex', 'condsoftmax', None]
|
||||
|
||||
@classmethod
|
||||
def newInvariantRatioEstimation(cls, classifier: BaseEstimator, val_split=5, n_jobs=None):
|
||||
"""
|
||||
Constructs a quantifier that implements the Invariant Ratio Estimator of
|
||||
`Vaz et al. 2018 <https://jmlr.org/papers/v20/18-456.html>_`. This amounts
|
||||
`Vaz et al. 2018 <https://jmlr.org/papers/v20/18-456.html>`_. This amounts
|
||||
to setting method to 'invariant-ratio' and clipping to 'project'.
|
||||
|
||||
:param classifier: a sklearn's Estimator that generates a classifier
|
||||
|
@ -451,15 +459,15 @@ class ACC(AggregativeCrispQuantifier):
|
|||
:param n_jobs: number of parallel workers
|
||||
:return: an instance of ACC configured so that it implements the Invariant Ratio Estimator
|
||||
"""
|
||||
return ACC(classifier, val_split=val_split, method='invariant-ratio', clipping='project', n_jobs=n_jobs)
|
||||
return ACC(classifier, val_split=val_split, method='invariant-ratio', norm='mapsimplex', n_jobs=n_jobs)
|
||||
|
||||
def _check_init_parameters(self):
|
||||
if self.solver not in ACC.SOLVERS:
|
||||
raise ValueError(f"unknown solver; valid ones are {ACC.SOLVERS}")
|
||||
if self.method not in ACC.METHODS:
|
||||
raise ValueError(f"unknown method; valid ones are {ACC.METHODS}")
|
||||
if self.clipping not in ACC.CLIPPING:
|
||||
raise ValueError(f"unknown clipping; valid ones are {ACC.CLIPPING}")
|
||||
if self.norm not in ACC.NORMALIZATIONS:
|
||||
raise ValueError(f"unknown clipping; valid ones are {ACC.NORMALIZATIONS}")
|
||||
|
||||
def aggregation_fit(self, classif_predictions: LabelledCollection, data: LabelledCollection):
|
||||
"""
|
||||
|
@ -497,12 +505,12 @@ class ACC(AggregativeCrispQuantifier):
|
|||
def aggregate(self, classif_predictions):
|
||||
prevs_estim = self.cc.aggregate(classif_predictions)
|
||||
estimate = F.solve_adjustment(
|
||||
p_c_cond_y=self.Pte_cond_estim_,
|
||||
p_c=prevs_estim,
|
||||
class_conditional_rates=self.Pte_cond_estim_,
|
||||
unadjusted_counts=prevs_estim,
|
||||
solver=self.solver,
|
||||
method=self.method,
|
||||
)
|
||||
return F.clip_prevalence(estimate, method=self.clipping)
|
||||
return F.normalize_prevalence(estimate, method=self.norm)
|
||||
|
||||
|
||||
class PACC(AggregativeSoftQuantifier):
|
||||
|
@ -521,21 +529,30 @@ class PACC(AggregativeSoftQuantifier):
|
|||
|
||||
:param str method: adjustment method to be used:
|
||||
|
||||
* 'inversion': matrix inversion method based on the matrix equality :math:`P(C)=P(C|Y)P(Y)`, which tries to invert `P(C|Y)` matrix.
|
||||
* 'invariant-ratio': invariant ratio estimator of `Vaz et al. <https://jmlr.org/papers/v20/18-456.html>`_, which replaces the last equation with the normalization condition.
|
||||
* 'inversion': matrix inversion method based on the matrix equality :math:`P(C)=P(C|Y)P(Y)`,
|
||||
which tries to invert `P(C|Y)` matrix.
|
||||
* 'invariant-ratio': invariant ratio estimator of `Vaz et al. <https://jmlr.org/papers/v20/18-456.html>`_,
|
||||
which replaces the last equation with the normalization condition.
|
||||
|
||||
:param str solver: the method to use for solving the system of linear equations. Valid options are:
|
||||
|
||||
* 'exact-raise': tries to solve the system using matrix inversion. Raises an error if the matrix has rank strictly less than `n_classes`.
|
||||
* 'exact-cc': if the matrix is not of full rank, returns `p_c` as the estimates, which corresponds to no adjustment (i.e., the classify and count method. See :class:`quapy.method.aggregative.CC`)
|
||||
* 'exact-raise': tries to solve the system using matrix inversion.
|
||||
Raises an error if the matrix has rank strictly less than `n_classes`.
|
||||
* 'exact-cc': if the matrix is not of full rank, returns `p_c` as the estimates, which
|
||||
corresponds to no adjustment (i.e., the classify and count method. See :class:`quapy.method.aggregative.CC`)
|
||||
* 'exact': deprecated, defaults to 'exact-cc'
|
||||
* 'minimize': minimizes the L2 norm of :math:`|Ax-B|`. This one generally works better, and is the default parameter. More details about this can be consulted in `Bunse, M. "On Multi-Class Extensions of Adjusted Classify and Count", on proceedings of the 2nd International Workshop on Learning to Quantify: Methods and Applications (LQ 2022), ECML/PKDD 2022, Grenoble (France) <https://lq-2022.github.io/proceedings/CompleteVolume.pdf>`_.
|
||||
* 'minimize': minimizes the L2 norm of :math:`|Ax-B|`. This one generally works better, and is the
|
||||
default parameter. More details about this can be consulted in `Bunse, M. "On Multi-Class Extensions
|
||||
of Adjusted Classify and Count", on proceedings of the 2nd International Workshop on Learning to
|
||||
Quantify: Methods and Applications (LQ 2022), ECML/PKDD 2022, Grenoble (France)
|
||||
<https://lq-2022.github.io/proceedings/CompleteVolume.pdf>`_.
|
||||
|
||||
:param str clipping: the method to use for normalization.
|
||||
:param str norm: the method to use for normalization.
|
||||
|
||||
* If `None` or `"none"`, no normalization is performed.
|
||||
* If `"clip"`, the values are clipped to the range [0,1] and normalized, so they sum up to 1.
|
||||
* If `"project"`, the values are projected onto the probability simplex.
|
||||
* `clip`, the values are clipped to the range [0,1] and then L1-normalized.
|
||||
* `mapsimplex` projects vectors onto the probability simplex. This implementation relies on
|
||||
`Mathieu Blondel's projection_simplex_sort <https://gist.github.com/mblondel/6f3b7aaad90606b98f71>`_
|
||||
* `condsoftmax`, applies a softmax normalization only to prevalence vectors that lie outside the simplex
|
||||
|
||||
:param n_jobs: number of parallel workers
|
||||
"""
|
||||
|
@ -545,24 +562,23 @@ class PACC(AggregativeSoftQuantifier):
|
|||
val_split=5,
|
||||
solver: Literal['minimize', 'exact', 'exact-raise', 'exact-cc'] = 'minimize',
|
||||
method: Literal['inversion', 'invariant-ratio'] = 'inversion',
|
||||
clipping: Literal['clip', 'none', 'project'] = 'clip',
|
||||
n_jobs=None,
|
||||
):
|
||||
norm: Literal['clip', 'mapsimplex', 'condsoftmax'] = 'clip',
|
||||
n_jobs=None
|
||||
):
|
||||
self.classifier = classifier
|
||||
self.val_split = val_split
|
||||
self.n_jobs = qp._get_njobs(n_jobs)
|
||||
|
||||
self.solver = solver
|
||||
self.method = method
|
||||
self.clipping = clipping
|
||||
self.norm = norm
|
||||
|
||||
def _check_init_parameters(self):
|
||||
if self.solver not in ACC.SOLVERS:
|
||||
raise ValueError(f"unknown solver; valid ones are {ACC.SOLVERS}")
|
||||
if self.method not in ACC.METHODS:
|
||||
raise ValueError(f"unknown method; valid ones are {ACC.METHODS}")
|
||||
if self.clipping not in ACC.CLIPPING:
|
||||
raise ValueError(f"unknown clipping; valid ones are {ACC.CLIPPING}")
|
||||
if self.clipping not in ACC.NORMALIZATIONS:
|
||||
raise ValueError(f"unknown clipping; valid ones are {ACC.NORMALIZATIONS}")
|
||||
|
||||
def aggregation_fit(self, classif_predictions: LabelledCollection, data: LabelledCollection):
|
||||
"""
|
||||
|
@ -580,12 +596,12 @@ class PACC(AggregativeSoftQuantifier):
|
|||
prevs_estim = self.pcc.aggregate(classif_posteriors)
|
||||
|
||||
estimate = F.solve_adjustment(
|
||||
p_c_cond_y=self.Pte_cond_estim_,
|
||||
p_c=prevs_estim,
|
||||
class_conditional_rates=self.Pte_cond_estim_,
|
||||
unadjusted_counts=prevs_estim,
|
||||
solver=self.solver,
|
||||
method=self.method,
|
||||
)
|
||||
return F.clip_prevalence(estimate, method=self.clipping)
|
||||
return F.normalize_prevalence(estimate, method=self.norm)
|
||||
|
||||
@classmethod
|
||||
def getPteCondEstim(cls, classes, y, y_):
|
||||
|
|
|
@ -25,6 +25,7 @@ class Status(Enum):
|
|||
|
||||
|
||||
class ConfigStatus:
|
||||
|
||||
def __init__(self, params, status, msg=''):
|
||||
self.params = params
|
||||
self.status = status
|
||||
|
|
Loading…
Reference in New Issue