diff --git a/README.md b/README.md index 265fe1a..03b4862 100644 --- a/README.md +++ b/README.md @@ -9,15 +9,16 @@ Label distribution learning (LDL) and label enhancement (LE) toolkit implemented + ([Yang, Sun, and Sun 2017](https://doi.org/10.1609/aaai.v31i1.10485)) [*AAAI*]: `BCPNN` and `ACPNN`. + ([Xu and Zhou 2017](https://doi.org/10.24963/ijcai.2017/443)) [*IJCAI*]: `IncomLDL`$^2$. + ([Shen et al. 2017](https://papers.nips.cc/paper_files/paper/2017/hash/6e2713a6efee97bacb63e52c54f0ada0-Abstract.html)) [*NeurIPS*]: `LDLF`. - + ([Wang and Geng 2019](https://doi.org/10.24963/ijcai.2019/515)) [*IJCAI*]: `LDL4C`$^3$. + + ([Jia et al. 2018](https://doi.org/10.1609/aaai.v32i1.11664)) [*AAAI*]: $^\dagger$`LDLLC`. + + ([Wang and Geng 2019](https://doi.org/10.24963/ijcai.2019/515)) [*IJCAI*]: $^\dagger$`LDL4C`$^3$. + ([Shen et al. 2020](https://dx.doi.org/10.14177/j.cnki.32-1397n.2020.44.06.004)) [*南京理工大学学报* (Chinese)]: `AdaBoostLDL`. + ([González et al. 2021a](https://doi.org/10.1016/j.ins.2020.07.071)) [*Inf. Sci.*]: `SSG_LDL`$^4$. + ([González et al. 2021b](https://doi.org/10.1016/j.inffus.2020.08.024)) [*Inf. Fusion*]: `DF_LDL`. - + ([Wang and Geng 2021a](https://doi.org/10.24963/ijcai.2021/426)) [*IJCAI*]: `LDL_HR`$^3$. - + ([Wang and Geng 2021b](https://proceedings.mlr.press/v139/wang21h.html)) [*ICML*]: `LDLM`$^3$. + + ([Wang and Geng 2021a](https://doi.org/10.24963/ijcai.2021/426)) [*IJCAI*]: $^\dagger$`LDL_HR`$^3$. + + ([Wang and Geng 2021b](https://proceedings.mlr.press/v139/wang21h.html)) [*ICML*]: $^\dagger$`LDLM`$^3$. + ([Jia et al. 2021](https://doi.org/10.1109/TKDE.2019.2943337)) [*TKDE*]: `LDL_SCL`. - + ([Jia et al. 2023a](https://doi.org/10.1109/TKDE.2021.3099294)) [*TKDE*]: `LDL_LRR`. - + ([Jia et al. 2023b](https://doi.org/10.1109/TNNLS.2023.3258976)) [*TNNLS*]: `LDL_DPA`. + + ([Jia et al. 2023a](https://doi.org/10.1109/TKDE.2021.3099294)) [*TKDE*]: $^\dagger$`LDL_LRR`. + + ([Jia et al. 2023b](https://doi.org/10.1109/TNNLS.2023.3258976)) [*TNNLS*]: $^\dagger$`LDL_DPA`. + ([Wen et al. 2023](https://doi.org/10.1109/ICCV51070.2023.02146)) [*ICCV*]: `CAD`$^1$, `QFD2`$^1$, and `CJS`$^1$. + ([Li and Chen 2024](https://doi.org/10.24963/ijcai.2024/494)) [*IJCAI*]: `WInLDL`$^2$. + ([Wu, Li, and Jia 2024](https://doi.org/10.1109/TBDATA.2024.3442562)) [*TBD*]: `LDL_DA`$^5$. @@ -28,7 +29,7 @@ Label distribution learning (LDL) and label enhancement (LE) toolkit implemented + LDL metrics: `chebyshev`, `clark`, `canberra`, `kl_divergence`, `cosine`, `intersection`, etc. + Structured LDL datasets: *Human_Gene*, *Movie*, *Natural_Scene*, *s-BU_3DFE*, *s-JAFFE*, *Yeast*, etc. + LDL applications: - + Facial emotion recognition (supported datasets: [*JAFFE*](https://zenodo.org/records/3451524) and [*BU-3DFE*](https://www.cs.binghamton.edu/~lijun/Research/3DFE/3DFE_Analysis.html)) + + Facial emotion recognition (supported datasets: [*JAFFE*](https://zenodo.org/records/3451524) and [*BU-3DFE*](https://www.cs.binghamton.edu/~lijun/Research/3DFE/3DFE_Analysis.html)). + ([Shirani et al. 2019](https://doi.org/10.18653/v1/P19-1112)) [*ACL*]: Emphasis selection (supported datasets: [*SemEval2020*](https://github.com/RiTUAL-UH/SemEval2020_Task10_Emphasis_Selection); pre-trained GloVe embeddings can be downloaded [here](https://nlp.stanford.edu/projects/glove/)). + ([Wu et al. 2019](https://doi.org/10.1109/ICCV.2019.01074)) [*ICCV*]: Lesion counting (supported datasets: [*ACNE04*](https://drive.google.com/drive/folders/18yJcHXhzOv7H89t-Lda6phheAicLqMuZ)). + ([Chen et al. 2020](https://doi.org/10.1109/CVPR42600.2020.01400)) [*CVPR*]: Facial emotion recognition with auxiliary label space graphs (supported datasets: [*CK+*](https://www.jeffcohn.net/Resources/); OpenFace can be downloaded [here](https://github.com/TadasBaltrusaitis/OpenFace/releases), and the required models can be downloaded [here](https://github.com/TadasBaltrusaitis/OpenFace/wiki/Model-download)). @@ -41,7 +42,9 @@ Label distribution learning (LDL) and label enhancement (LE) toolkit implemented > > $^4$ These are oversampling algorithms for LDL, therefore you should use `fit_transform` to generate synthetic samples. > -> $^5$: To use domain adaptation methods for LDL, you need to provide the source domain data via parameters `sX` and `sy` of the `fit` method. [Here](https://github.com/SpriteMisaka/PyLDL/blob/main/demo/domain_adaptation.ipynb) is a demo on domain adaptation for LDL. +> $^5$ To use domain adaptation methods for LDL, you need to provide the source domain data via parameters `sX` and `sy` of the `fit` method. [Here](https://github.com/SpriteMisaka/PyLDL/blob/main/demo/domain_adaptation.ipynb) is a demo on domain adaptation for LDL. + +> $^\dagger$ These methods involve imposing constraints on model parameters, like regularization. Therefore, it is recommended to carefully tune the hyperparameters and apply feature preprocessing techniques like `StandardScaler` or `MinMaxScaler` before conducting experiments to achieve the expected performance. ## Installation diff --git a/docs/genindex.html b/docs/genindex.html index 2e7024a..faf26e5 100644 --- a/docs/genindex.html +++ b/docs/genindex.html @@ -98,6 +98,8 @@

L

  • LDL_SCL (class in pyldl.algorithms)
  • LDLF (class in pyldl.algorithms) +
  • +
  • LDLLC (class in pyldl.algorithms)
  • LDLM (class in pyldl.algorithms)
  • @@ -127,8 +129,6 @@

    P

    • pairwise_cosine() (pyldl.algorithms.LDL_DA static method) -
    • -
    • pairwise_euclidean() (pyldl.algorithms.LDL_DA static method)
    • pairwise_jsd() (pyldl.algorithms.LDL_DA static method)
    • diff --git a/docs/index.html b/docs/index.html index 01c4fb9..9b67be6 100644 --- a/docs/index.html +++ b/docs/index.html @@ -54,6 +54,12 @@

      PyLDL documentation +
      +class pyldl.algorithms.LDLLC(*args, **kwargs)
      +

      LDLLC is proposed in paper Label Distribution Learning by Exploiting Label Correlations.

      +
      +
      class pyldl.algorithms.LDLM(*args, **kwargs)
      @@ -133,26 +139,6 @@

      PyLDL documentation -
      -static pairwise_euclidean(X: Tensor, Y: Tensor) Tensor
      -

      Pairwise Euclidean distance.

      -
      -
      Parameters:
      -
        -
      • X (tf.Tensor) – Matrix \(\boldsymbol{X}\) (shape: \([m_X,\, n_X]\)).

      • -
      • Y (tf.Tensor) – Matrix \(\boldsymbol{Y}\) (shape: \([m_Y,\, n_Y]\)).

      • -
      -
      -
      Returns:
      -

      Pairwise Euclidean distance (shape: \([m_X,\, m_Y]\)).

      -
      -
      Return type:
      -

      tf.Tensor

      -
      -
      -

      -
      static pairwise_jsd(X: Tensor, Y: Tensor) Tensor
      diff --git a/docs/objects.inv b/docs/objects.inv index 477aef4..4243748 100644 --- a/docs/objects.inv +++ b/docs/objects.inv @@ -2,6 +2,9 @@ # Project: PyLDL # Version: # The remainder of this file is compressed using zlib. -xڥOo Mk-ۦ13IWB~RЛJyy˚#vPaY/ -A*KH98{kse7b3Tz2Mm =g>[1Npn\wHJDUa(PaF [x1Hj8~ٟ;_bFK>p =R.:D+c ǤS% #W֌nj63o O2v2)iSNٰ&?:u[K+@]ЮYfo):A0 \ No newline at end of file +xڥOo0| + +ْm3Hv$}.%-dۏNzӾ޾e ˄U^(lV 5ŧoW{ׂg?8E4REg6vu1n݉YN\{DJDUb(ߊ<3tvAdڸÉcܻ9Zk,DA. iV.B88yRT_I% +fn'0à=ɍbDӷF,;ӗ©KNٴ,:<ٶ]R1<$fY49G骹08ӶF[ۏ& +Ȫvq?zxx~ik=9QEMfe\ +=j=-> !د̡'(DC \ No newline at end of file diff --git a/docs/searchindex.js b/docs/searchindex.js index 6f0092c..dfc09fa 100644 --- a/docs/searchindex.js +++ b/docs/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles": {"PyLDL documentation": [[0, null]]}, "docnames": ["index"], "envversion": {"sphinx": 62, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst"], "indexentries": {"_base (class in pyldl.algorithms.base)": [[0, "pyldl.algorithms.base._Base", false]], "augment() (pyldl.algorithms.ldl_da static method)": [[0, "pyldl.algorithms.LDL_DA.augment", false]], "fit() (pyldl.algorithms.base._base method)": [[0, "pyldl.algorithms.base._Base.fit", false]], "fit() (pyldl.algorithms.ldl_da method)": [[0, "pyldl.algorithms.LDL_DA.fit", false]], "incomldl (class in pyldl.algorithms)": [[0, "pyldl.algorithms.IncomLDL", false]], "ldl4c (class in pyldl.algorithms)": [[0, "pyldl.algorithms.LDL4C", false]], "ldl_da (class in pyldl.algorithms)": [[0, "pyldl.algorithms.LDL_DA", false]], "ldl_dpa (class in pyldl.algorithms)": [[0, "pyldl.algorithms.LDL_DPA", false]], "ldl_hr (class in pyldl.algorithms)": [[0, "pyldl.algorithms.LDL_HR", false]], "ldl_lrr (class in pyldl.algorithms)": [[0, "pyldl.algorithms.LDL_LRR", false]], "ldl_scl (class in pyldl.algorithms)": [[0, "pyldl.algorithms.LDL_SCL", false]], "ldlf (class in pyldl.algorithms)": [[0, "pyldl.algorithms.LDLF", false]], "ldlm (class in pyldl.algorithms)": [[0, "pyldl.algorithms.LDLM", false]], "module": [[0, "module-pyldl.algorithms", false], [0, "module-pyldl.algorithms.base", false], [0, "module-pyldl.metrics", false], [0, "module-pyldl.utils", false]], "pairwise_cosine() (pyldl.algorithms.ldl_da static method)": [[0, "pyldl.algorithms.LDL_DA.pairwise_cosine", false]], "pairwise_euclidean() (pyldl.algorithms.ldl_da static method)": [[0, "pyldl.algorithms.LDL_DA.pairwise_euclidean", false]], "pairwise_jsd() (pyldl.algorithms.ldl_da static method)": [[0, "pyldl.algorithms.LDL_DA.pairwise_jsd", false]], "pairwise_label() (pyldl.algorithms.ldl_da static method)": [[0, "pyldl.algorithms.LDL_DA.pairwise_label", false]], "pyldl.algorithms": [[0, "module-pyldl.algorithms", false]], "pyldl.algorithms.base": [[0, "module-pyldl.algorithms.base", false]], "pyldl.metrics": [[0, "module-pyldl.metrics", false]], "pyldl.utils": [[0, "module-pyldl.utils", false]], "reorder_y() (pyldl.algorithms.ldl_da static method)": [[0, "pyldl.algorithms.LDL_DA.reorder_y", false]], "winldl (class in pyldl.algorithms)": [[0, "pyldl.algorithms.WInLDL", false]]}, "objects": {"pyldl": [[0, 0, 0, "-", "algorithms"], [0, 0, 0, "-", "metrics"], [0, 0, 0, "-", "utils"]], "pyldl.algorithms": [[0, 1, 1, "", "IncomLDL"], [0, 1, 1, "", "LDL4C"], [0, 1, 1, "", "LDLF"], [0, 1, 1, "", "LDLM"], [0, 1, 1, "", "LDL_DA"], [0, 1, 1, "", "LDL_DPA"], [0, 1, 1, "", "LDL_HR"], [0, 1, 1, "", "LDL_LRR"], [0, 1, 1, "", "LDL_SCL"], [0, 1, 1, "", "WInLDL"], [0, 0, 0, "-", "base"]], "pyldl.algorithms.LDL_DA": [[0, 2, 1, "", "augment"], [0, 2, 1, "", "fit"], [0, 2, 1, "", "pairwise_cosine"], [0, 2, 1, "", "pairwise_euclidean"], [0, 2, 1, "", "pairwise_jsd"], [0, 2, 1, "", "pairwise_label"], [0, 2, 1, "", "reorder_y"]], "pyldl.algorithms.base": [[0, 1, 1, "", "_Base"]], "pyldl.algorithms.base._Base": [[0, 2, 1, "", "fit"]]}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"], "2": ["py", "method", "Python method"]}, "objtypes": {"0": "py:module", "1": "py:class", "2": "py:method"}, "terms": {"0": 0, "01": 0, "1000": 0, "1e": 0, "2": 0, "If": 0, "No": 0, "_base": 0, "adapt": 0, "add": 0, "algorithm": 0, "align": 0, "all": 0, "alpha": 0, "ar": 0, "arg": 0, "augment": 0, "base": 0, "beta": 0, "boldsymbol": 0, "bool": 0, "callback": 0, "class": 0, "classif": 0, "comparison": 0, "consist": 0, "content": 0, "contrast": 0, "control": 0, "correl": 0, "cosin": 0, "da": 0, "data": 0, "default": 0, "degre": 0, "descript": 0, "detail": 0, "distanc": 0, "distribut": 0, "diverg": 0, "domain": 0, "dpa": 0, "effect": 0, "effici": 0, "epoch": 0, "euclidean": 0, "fals": 0, "featur": 0, "fine": 0, "fine_tun": 0, "fit": 0, "float": 0, "forest": 0, "ft_epoch": 0, "ft_optim": 0, "highest": 0, "hr": 0, "hyperparamet": 0, "i": 0, "incomldl": 0, "incomplet": 0, "int": 0, "jensen": 0, "kera": 0, "kwarg": 0, "label": 0, "ldl": 0, "ldl4c": 0, "ldl_da": 0, "ldl_dpa": 0, "ldl_hr": 0, "ldl_lrr": 0, "ldl_scl": 0, "ldlf": 0, "ldlm": 0, "learn": 0, "local": 0, "loss": 0, "lrr": 0, "m_": 0, "m_t": 0, "m_x": 0, "m_y": 0, "machin": 0, "maintain": 0, "margin": 0, "matrix": 0, "max": 0, "measur": 0, "model": 0, "n_": 0, "n_t": 0, "n_x": 0, "n_y": 0, "ndarrai": 0, "need": 0, "new": 0, "none": 0, "np": 0, "number": 0, "optim": 0, "option": 0, "order": 0, "orient": 0, "otherwis": 0, "pairwis": 0, "pairwise_cosin": 0, "pairwise_euclidean": 0, "pairwise_jsd": 0, "pairwise_label": 0, "paper": 0, "paramet": 0, "propos": 0, "prototyp": 0, "r": 0, "random_st": 0, "rank": 0, "regular": 0, "relat": 0, "reorder": 0, "reorder_i": 0, "respect": 0, "rest": 0, "restructuredtext": 0, "return": 0, "same": 0, "sampl": 0, "scl": 0, "see": 0, "semant": 0, "shannon": 0, "shape": 0, "similar": 0, "sourc": 0, "src": 0, "static": 0, "sx": 0, "sy": 0, "syntax": 0, "target": 0, "tensor": 0, "tf": 0, "tgt": 0, "true": 0, "tune": 0, "tupl": 0, "two": 0, "tx": 0, "ty": 0, "type": 0, "us": 0, "weight": 0, "whether": 0, "winldl": 0, "x": 0, "x_val": 0, "y": 0, "y_val": 0, "your": 0}, "titles": ["PyLDL documentation"], "titleterms": {"document": 0, "pyldl": 0}}) \ No newline at end of file +Search.setIndex({"alltitles": {"PyLDL documentation": [[0, null]]}, "docnames": ["index"], "envversion": {"sphinx": 62, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst"], "indexentries": {"_base (class in pyldl.algorithms.base)": [[0, "pyldl.algorithms.base._Base", false]], "augment() (pyldl.algorithms.ldl_da static method)": [[0, "pyldl.algorithms.LDL_DA.augment", false]], "fit() (pyldl.algorithms.base._base method)": [[0, "pyldl.algorithms.base._Base.fit", false]], "fit() (pyldl.algorithms.ldl_da method)": [[0, "pyldl.algorithms.LDL_DA.fit", false]], "incomldl (class in pyldl.algorithms)": [[0, "pyldl.algorithms.IncomLDL", false]], "ldl4c (class in pyldl.algorithms)": [[0, "pyldl.algorithms.LDL4C", false]], "ldl_da (class in pyldl.algorithms)": [[0, "pyldl.algorithms.LDL_DA", false]], "ldl_dpa (class in pyldl.algorithms)": [[0, "pyldl.algorithms.LDL_DPA", false]], "ldl_hr (class in pyldl.algorithms)": [[0, "pyldl.algorithms.LDL_HR", false]], "ldl_lrr (class in pyldl.algorithms)": [[0, "pyldl.algorithms.LDL_LRR", false]], "ldl_scl (class in pyldl.algorithms)": [[0, "pyldl.algorithms.LDL_SCL", false]], "ldlf (class in pyldl.algorithms)": [[0, "pyldl.algorithms.LDLF", false]], "ldllc (class in pyldl.algorithms)": [[0, "pyldl.algorithms.LDLLC", false]], "ldlm (class in pyldl.algorithms)": [[0, "pyldl.algorithms.LDLM", false]], "module": [[0, "module-pyldl.algorithms", false], [0, "module-pyldl.algorithms.base", false], [0, "module-pyldl.metrics", false], [0, "module-pyldl.utils", false]], "pairwise_cosine() (pyldl.algorithms.ldl_da static method)": [[0, "pyldl.algorithms.LDL_DA.pairwise_cosine", false]], "pairwise_jsd() (pyldl.algorithms.ldl_da static method)": [[0, "pyldl.algorithms.LDL_DA.pairwise_jsd", false]], "pairwise_label() (pyldl.algorithms.ldl_da static method)": [[0, "pyldl.algorithms.LDL_DA.pairwise_label", false]], "pyldl.algorithms": [[0, "module-pyldl.algorithms", false]], "pyldl.algorithms.base": [[0, "module-pyldl.algorithms.base", false]], "pyldl.metrics": [[0, "module-pyldl.metrics", false]], "pyldl.utils": [[0, "module-pyldl.utils", false]], "reorder_y() (pyldl.algorithms.ldl_da static method)": [[0, "pyldl.algorithms.LDL_DA.reorder_y", false]], "winldl (class in pyldl.algorithms)": [[0, "pyldl.algorithms.WInLDL", false]]}, "objects": {"pyldl": [[0, 0, 0, "-", "algorithms"], [0, 0, 0, "-", "metrics"], [0, 0, 0, "-", "utils"]], "pyldl.algorithms": [[0, 1, 1, "", "IncomLDL"], [0, 1, 1, "", "LDL4C"], [0, 1, 1, "", "LDLF"], [0, 1, 1, "", "LDLLC"], [0, 1, 1, "", "LDLM"], [0, 1, 1, "", "LDL_DA"], [0, 1, 1, "", "LDL_DPA"], [0, 1, 1, "", "LDL_HR"], [0, 1, 1, "", "LDL_LRR"], [0, 1, 1, "", "LDL_SCL"], [0, 1, 1, "", "WInLDL"], [0, 0, 0, "-", "base"]], "pyldl.algorithms.LDL_DA": [[0, 2, 1, "", "augment"], [0, 2, 1, "", "fit"], [0, 2, 1, "", "pairwise_cosine"], [0, 2, 1, "", "pairwise_jsd"], [0, 2, 1, "", "pairwise_label"], [0, 2, 1, "", "reorder_y"]], "pyldl.algorithms.base": [[0, 1, 1, "", "_Base"]], "pyldl.algorithms.base._Base": [[0, 2, 1, "", "fit"]]}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"], "2": ["py", "method", "Python method"]}, "objtypes": {"0": "py:module", "1": "py:class", "2": "py:method"}, "terms": {"0": 0, "01": 0, "1000": 0, "1e": 0, "2": 0, "If": 0, "No": 0, "_base": 0, "adapt": 0, "add": 0, "algorithm": 0, "align": 0, "all": 0, "alpha": 0, "ar": 0, "arg": 0, "augment": 0, "base": 0, "beta": 0, "boldsymbol": 0, "bool": 0, "callback": 0, "class": 0, "classif": 0, "comparison": 0, "consist": 0, "content": 0, "contrast": 0, "control": 0, "correl": 0, "cosin": 0, "da": 0, "data": 0, "default": 0, "degre": 0, "descript": 0, "detail": 0, "distanc": 0, "distribut": 0, "diverg": 0, "domain": 0, "dpa": 0, "effect": 0, "effici": 0, "epoch": 0, "euclidean": 0, "exploit": 0, "fals": 0, "featur": 0, "fine": 0, "fine_tun": 0, "fit": 0, "float": 0, "forest": 0, "ft_epoch": 0, "ft_optim": 0, "highest": 0, "hr": 0, "hyperparamet": 0, "i": 0, "incomldl": 0, "incomplet": 0, "int": 0, "jensen": 0, "kera": 0, "kwarg": 0, "label": 0, "ldl": 0, "ldl4c": 0, "ldl_da": 0, "ldl_dpa": 0, "ldl_hr": 0, "ldl_lrr": 0, "ldl_scl": 0, "ldlf": 0, "ldllc": 0, "ldlm": 0, "learn": 0, "local": 0, "loss": 0, "lrr": 0, "m_": 0, "m_t": 0, "m_x": 0, "m_y": 0, "machin": 0, "maintain": 0, "margin": 0, "matrix": 0, "max": 0, "measur": 0, "model": 0, "n_": 0, "n_t": 0, "n_x": 0, "n_y": 0, "ndarrai": 0, "need": 0, "new": 0, "none": 0, "np": 0, "number": 0, "optim": 0, "option": 0, "order": 0, "orient": 0, "otherwis": 0, "pairwis": 0, "pairwise_cosin": 0, "pairwise_euclidean": [], "pairwise_jsd": 0, "pairwise_label": 0, "paper": 0, "paramet": 0, "propos": 0, "prototyp": 0, "r": 0, "random_st": 0, "rank": 0, "regular": 0, "relat": 0, "reorder": 0, "reorder_i": 0, "respect": 0, "rest": 0, "restructuredtext": 0, "return": 0, "same": 0, "sampl": 0, "scl": 0, "see": 0, "semant": 0, "shannon": 0, "shape": 0, "similar": 0, "sourc": 0, "src": 0, "static": 0, "sx": 0, "sy": 0, "syntax": 0, "target": 0, "tensor": 0, "tf": 0, "tgt": 0, "true": 0, "tune": 0, "tupl": 0, "two": 0, "tx": 0, "ty": 0, "type": 0, "us": 0, "weight": 0, "whether": 0, "winldl": 0, "x": 0, "x_val": 0, "y": 0, "y_val": 0, "your": 0}, "titles": ["PyLDL documentation"], "titleterms": {"document": 0, "pyldl": 0}}) \ No newline at end of file diff --git a/pyldl/algorithms/__init__.py b/pyldl/algorithms/__init__.py index 5524720..c207a37 100644 --- a/pyldl/algorithms/__init__.py +++ b/pyldl/algorithms/__init__.py @@ -16,6 +16,7 @@ from ._ensemble import DF_LDL, AdaBoostLDL from ._ldlf import LDLF +from ._ldllc import LDLLC from ._ldl_scl import LDL_SCL from ._ldl_lrr import LDL_LRR from ._ldl_dpa import LDL_DPA @@ -29,7 +30,7 @@ __all__ = ["SA_BFGS", "SA_IIS", "AA_KNN", "AA_BP", "PT_Bayes", "PT_SVM", "CPNN", "BCPNN", "ACPNN", "LDSVR", - "LDLF", "LDL_SCL", "LDL_LRR", "LDL_DPA", "CAD", "QFD2", "CJS", + "LDLF", "LDLLC", "LDL_SCL", "LDL_LRR", "LDL_DPA", "CAD", "QFD2", "CJS", "DF_LDL", "AdaBoostLDL", "LDL4C", "LDL_HR", "LDLM", "IncomLDL", "WInLDL", diff --git a/pyldl/algorithms/_algorithm_adaptation.py b/pyldl/algorithms/_algorithm_adaptation.py index 9c648da..88784d5 100644 --- a/pyldl/algorithms/_algorithm_adaptation.py +++ b/pyldl/algorithms/_algorithm_adaptation.py @@ -3,8 +3,8 @@ import keras import tensorflow as tf -from keras import backend as K +from pyldl.algorithms.utils import RProp from pyldl.algorithms.base import BaseLDL, BaseDeepLDL, BaseGD, BaseAdam @@ -70,69 +70,6 @@ def _CJS(y, y_pred): ) -class RProp(keras.optimizers.Optimizer): - - def __init__(self, init_alpha=1e-3, scale_up=1.2, scale_down=0.5, min_alpha=1e-6, max_alpha=50., **kwargs): - super(RProp, self).__init__(name='rprop', **kwargs) - self.init_alpha = K.variable(init_alpha, name='init_alpha') - self.scale_up = K.variable(scale_up, name='scale_up') - self.scale_down = K.variable(scale_down, name='scale_down') - self.min_alpha = K.variable(min_alpha, name='min_alpha') - self.max_alpha = K.variable(max_alpha, name='max_alpha') - - def apply_gradients(self, grads_and_vars): - grads, trainable_variables = zip(*grads_and_vars) - self.get_updates(trainable_variables, grads) - - def get_updates(self, params, gradients): - grads = gradients - shapes = [K.int_shape(p) for p in params] - alphas = [K.variable(np.ones(shape) * self.init_alpha) for shape in shapes] - old_grads = [K.zeros(shape) for shape in shapes] - prev_weight_deltas = [K.zeros(shape) for shape in shapes] - self.updates = [] - - for param, grad, old_grad, prev_weight_delta, alpha in zip(params, grads, - old_grads, prev_weight_deltas, - alphas): - - new_alpha = K.switch( - K.greater(grad * old_grad, 0), - K.minimum(alpha * self.scale_up, self.max_alpha), - K.switch(K.less(grad * old_grad, 0), K.maximum(alpha * self.scale_down, self.min_alpha), alpha) - ) - - new_delta = K.switch(K.greater(grad, 0), - -new_alpha, - K.switch(K.less(grad, 0), - new_alpha, - K.zeros_like(new_alpha))) - - weight_delta = K.switch(K.less(grad*old_grad, 0), -prev_weight_delta, new_delta) - - new_param = param + weight_delta - - grad = K.switch(K.less(grad*old_grad, 0), K.zeros_like(grad), grad) - - self.updates.append(K.update(param, new_param)) - self.updates.append(K.update(alpha, new_alpha)) - self.updates.append(K.update(old_grad, grad)) - self.updates.append(K.update(prev_weight_delta, weight_delta)) - - return self.updates - - def get_config(self): - config = { - 'init_alpha': float(K.get_value(self.init_alpha)), - 'scale_up': float(K.get_value(self.scale_up)), - 'scale_down': float(K.get_value(self.scale_down)), - 'min_alpha': float(K.get_value(self.min_alpha)), - 'max_alpha': float(K.get_value(self.max_alpha)), - } - base_config = super(RProp, self).get_config() - return dict(list(base_config.items()) + list(config.items())) - - class CPNN(BaseGD, BaseDeepLDL): def _not_proper_mode(self): diff --git a/pyldl/algorithms/_classifier.py b/pyldl/algorithms/_classifier.py index ebdacdd..f1237ce 100644 --- a/pyldl/algorithms/_classifier.py +++ b/pyldl/algorithms/_classifier.py @@ -1,26 +1,32 @@ +import numpy as np + import keras import tensorflow as tf from pyldl.algorithms.base import BaseDeepLDLClassifier, BaseGD, BaseBFGS +EPS = np.finfo(np.float32).eps + + class LDL4C(BaseBFGS, BaseDeepLDLClassifier): """LDL4C is proposed in paper *Classification with Label Distribution Learning*. """ @tf.function def _loss(self, params_1d): - y_pred = keras.activations.softmax(self._X @ self._params2model(params_1d)[0]) + theta = self._params2model(params_1d)[0] + y_pred = keras.activations.softmax(self._X @ theta) top2 = tf.gather(y_pred, self._top2, axis=1, batch_dims=1) margin = tf.reduce_sum(tf.maximum(0., 1. - (top2[:, 0] - top2[:, 1]) / self._rho)) mae = keras.losses.mean_absolute_error(self._y, y_pred) - return tf.reduce_sum(self._entropy * mae) + self._alpha * margin + self._beta * self._l2_reg(self._model) + return tf.reduce_sum(self._entropy * mae) + self._alpha * margin + self._beta * self._l2_reg(theta) def _before_train(self): self._top2 = tf.math.top_k(self._y, k=2)[1] - self._entropy = tf.cast(-tf.reduce_sum(self._y * tf.math.log(self._y) + 1e-7, axis=1), dtype=tf.float32) + self._entropy = tf.cast(-tf.reduce_sum(self._y * tf.math.log(self._y) + EPS, axis=1), dtype=tf.float32) - def fit(self, X, y, alpha=1e-2, beta=1e-6, rho=1e-2, **kwargs): + def fit(self, X, y, alpha=1e-2, beta=0., rho=1e-2, **kwargs): self._alpha = alpha self._beta = beta self._rho = rho @@ -33,7 +39,8 @@ class LDL_HR(BaseBFGS, BaseDeepLDLClassifier): @tf.function def _loss(self, params_1d): - y_pred = keras.activations.softmax(self._X @ self._params2model(params_1d)[0]) + theta = self._params2model(params_1d)[0] + y_pred = keras.activations.softmax(self._X @ theta) highest = tf.gather(y_pred, self._highest, axis=1, batch_dims=1) rest = tf.gather(y_pred, self._rest, axis=1, batch_dims=1) @@ -44,7 +51,7 @@ def _loss(self, params_1d): mae = tf.reduce_sum(keras.losses.mean_absolute_error(self._l, y_pred)) - return mae + self._alpha * margin + self._beta * rest_mae + self._gamma * self._l2_reg(self._model) + return mae + self._alpha * margin + self._beta * rest_mae + self._gamma * self._l2_reg(theta) def _before_train(self): temp = tf.math.top_k(self._y, k=self._n_outputs)[1] @@ -52,7 +59,7 @@ def _before_train(self): self._rest = temp[:, 1:] self._l = tf.one_hot(tf.reshape(self._highest, -1), self._n_outputs) - def fit(self, X, y, alpha=1e-2, beta=1e-2, gamma=1e-6, rho=1e-2, **kwargs): + def fit(self, X, y, alpha=1e-2, beta=1e-2, gamma=0., rho=1e-2, **kwargs): self._alpha = alpha self._beta = beta self._gamma = gamma @@ -97,7 +104,7 @@ def _before_train(self): def _get_default_model(self): return self.get_2layer_model(self._n_features, self._n_outputs) - def fit(self, X, y, alpha=1e-2, beta=1e-2, gamma=1e-6, rho=1e-2, **kwargs): + def fit(self, X, y, alpha=1e-2, beta=1e-2, gamma=0., rho=1e-2, **kwargs): self._alpha = alpha self._beta = beta self._gamma = gamma diff --git a/pyldl/algorithms/_ldl_da.py b/pyldl/algorithms/_ldl_da.py index 355ed9c..d705e11 100644 --- a/pyldl/algorithms/_ldl_da.py +++ b/pyldl/algorithms/_ldl_da.py @@ -5,6 +5,7 @@ from sklearn.preprocessing import MinMaxScaler import tensorflow as tf +from pyldl.algorithms.utils import pairwise_euclidean from pyldl.algorithms.base import BaseDeepLDL, BaseAdam @@ -62,22 +63,6 @@ def pairwise_jsd(X: tf.Tensor, Y: tf.Tensor) -> tf.Tensor: js = 0.5 * (kl(temp1, temp3) + kl(temp2, temp3)) return js - @staticmethod - def pairwise_euclidean(X: tf.Tensor, Y: tf.Tensor) -> tf.Tensor: - """Pairwise Euclidean distance. - - :param X: Matrix :math:`\\boldsymbol{X}` (shape: :math:`[m_X,\, n_X]`). - :type X: tf.Tensor - :param Y: Matrix :math:`\\boldsymbol{Y}` (shape: :math:`[m_Y,\, n_Y]`). - :type Y: tf.Tensor - :return: Pairwise Euclidean distance (shape: :math:`[m_X,\, m_Y]`). - :rtype: tf.Tensor - """ - X2 = tf.tile(tf.reduce_sum(tf.square(X), axis=1, keepdims=True), [1, tf.shape(Y)[0]]) - Y2 = tf.tile(tf.reduce_sum(tf.square(Y), axis=1, keepdims=True), [1, tf.shape(X)[0]]) - XY = tf.matmul(X, tf.transpose(Y)) - return X2 + tf.transpose(Y2) - 2 * XY - @staticmethod def pairwise_cosine(X: tf.Tensor, Y: tf.Tensor) -> tf.Tensor: """Pairwise cosine similarity. @@ -149,7 +134,7 @@ def _loss(self, sX, sy, start, end): mse = self.loss_function(sy, sy_pred) mse += self.loss_function(self._ty, ty_pred) - dis_X = self.pairwise_euclidean(sfeatures, tfeatures) + dis_X = pairwise_euclidean(sfeatures, tfeatures) sim_X = tf.maximum(self._margin - dis_X, 0.) if self._margin else self.pairwise_cosine(sfeatures, tfeatures) con = tf.reduce_sum(self._hw[start:end] * dis_X) / (tf.reduce_sum(self._hmask_y[start:end]) + EPS) diff --git a/pyldl/algorithms/_ldl_dpa.py b/pyldl/algorithms/_ldl_dpa.py index f1808ca..e793032 100644 --- a/pyldl/algorithms/_ldl_dpa.py +++ b/pyldl/algorithms/_ldl_dpa.py @@ -24,16 +24,17 @@ def disvar(y, y_pred): @tf.function def _loss(self, params_1d): - y_pred = keras.activations.softmax(self._X @ self._params2model(params_1d)[0]) + theta = self._params2model(params_1d)[0] + y_pred = keras.activations.softmax(self._X @ theta) kld = tf.math.reduce_sum(keras.losses.kl_divergence(self._y, y_pred)) rnkdpa = self.rnkdpa(self._R, y_pred) disvar = self.disvar(self._y, y_pred) - return kld + self._alpha * rnkdpa + self._beta * disvar + self._gamma * self._l2_reg(self._model) + return kld + self._alpha * rnkdpa + self._beta * disvar + self._gamma * self._l2_reg(theta) def _before_train(self): self._R = tf.cast(rankdata(self._y, axis=1), tf.float32) - def fit(self, X, y, alpha=1e-2, beta=1e-2, gamma=1e-6, **kwargs): + def fit(self, X, y, alpha=1e-2, beta=1e-2, gamma=0., **kwargs): self._alpha = alpha self._beta = beta self._gamma = gamma diff --git a/pyldl/algorithms/_ldl_lrr.py b/pyldl/algorithms/_ldl_lrr.py index 11e374f..9d98560 100644 --- a/pyldl/algorithms/_ldl_lrr.py +++ b/pyldl/algorithms/_ldl_lrr.py @@ -1,9 +1,14 @@ +import numpy as np + import keras import tensorflow as tf from pyldl.algorithms.base import BaseDeepLDL, BaseBFGS +EPS = np.finfo(np.float32).eps + + class LDL_LRR(BaseBFGS, BaseDeepLDL): """LDL-LRR is proposed in paper *Label Distribution Learning by Maintaining Label Ranking Relation*. """ @@ -11,29 +16,29 @@ class LDL_LRR(BaseBFGS, BaseDeepLDL): @staticmethod @tf.function def ranking_loss(y_pred, P, W): - Phat = tf.math.sigmoid((tf.expand_dims(y_pred, -1) - tf.expand_dims(y_pred, 1)) * 100) - l = ((1 - P) * tf.math.log(tf.clip_by_value(1 - Phat, 1e-9, 1.0)) + \ - P * tf.math.log(tf.clip_by_value(Phat, 1e-9, 1.0))) * W + Phat = tf.math.sigmoid(y_pred[:, :, None] - y_pred[:, None, :]) + l = ((1 - P) * tf.math.log(tf.clip_by_value(1 - Phat, EPS, 1.)) +\ + P * tf.math.log(tf.clip_by_value(Phat, EPS, 1.))) * W return -tf.reduce_sum(l) @staticmethod @tf.function def preprocessing(y): - P = tf.where(tf.nn.sigmoid(tf.expand_dims(y, -1) - tf.expand_dims(y, 1)) > .5, 1., 0.) - W = tf.square(tf.expand_dims(y, -1) - tf.expand_dims(y, 1)) - return P, W + diff = y[:, :, None] - y[:, None, :] + return tf.where(diff > .5, 1., 0.), tf.square(diff) @tf.function def _loss(self, params_1d): - y_pred = keras.activations.softmax(self._X @ self._params2model(params_1d)[0]) + theta = self._params2model(params_1d)[0] + y_pred = keras.activations.softmax(self._X @ theta) kld = tf.math.reduce_mean(keras.losses.kl_divergence(self._y, y_pred)) rnk = self.ranking_loss(y_pred, self._P, self._W) / (2 * self._X.shape[0]) - return kld + self._alpha * rnk + self._beta * self._l2_reg(self._model) + return kld + self._alpha * rnk + self._beta * self._l2_reg(theta) def _before_train(self): self._P, self._W = self.preprocessing(self._y) - def fit(self, X, y, alpha=1e-2, beta=1e-6, **kwargs): + def fit(self, X, y, alpha=1e-2, beta=0., **kwargs): self._alpha = alpha self._beta = beta return super().fit(X, y, **kwargs) diff --git a/pyldl/algorithms/_ldllc.py b/pyldl/algorithms/_ldllc.py new file mode 100644 index 0000000..0f4def1 --- /dev/null +++ b/pyldl/algorithms/_ldllc.py @@ -0,0 +1,31 @@ +import numpy as np + +import keras +import tensorflow as tf +import tensorflow_probability as tfp + +from pyldl.algorithms.utils import pairwise_euclidean +from pyldl.algorithms.base import BaseDeepLDL, BaseBFGS + + +EPS = np.finfo(np.float32).eps + + +class LDLLC(BaseBFGS, BaseDeepLDL): + """LDLLC is proposed in paper *Label Distribution Learning by Exploiting Label Correlations*. + """ + + @tf.function + def _loss(self, params_1d): + theta = self._params2model(params_1d)[0] + y_pred = keras.activations.softmax(self._X @ theta) + kld = tf.reduce_sum(keras.losses.kl_divergence(self._y, y_pred)) + lc = tfp.stats.correlation(theta) * pairwise_euclidean(tf.transpose(theta)) + eye = tf.eye(self._n_outputs, dtype=tf.float32) + lc = tf.reduce_sum(lc * (1 - eye)) / 2. + return kld + self._alpha * lc + self._beta * self._l2_reg(theta) + + def fit(self, X, y, alpha=1e-3, beta=0., **kwargs): + self._alpha = alpha + self._beta = beta + return super().fit(X, y, **kwargs) diff --git a/pyldl/algorithms/_problem_transformation.py b/pyldl/algorithms/_problem_transformation.py index ffa0a2e..dc127ca 100644 --- a/pyldl/algorithms/_problem_transformation.py +++ b/pyldl/algorithms/_problem_transformation.py @@ -2,7 +2,6 @@ from scipy.spatial.distance import pdist -from sklearn.base import BaseEstimator from sklearn.svm import LinearSVC, SVR from sklearn.naive_bayes import GaussianNB from sklearn.multioutput import MultiOutputRegressor diff --git a/pyldl/algorithms/_specialized_algorithms.py b/pyldl/algorithms/_specialized_algorithms.py index 392db88..bb411cc 100644 --- a/pyldl/algorithms/_specialized_algorithms.py +++ b/pyldl/algorithms/_specialized_algorithms.py @@ -5,6 +5,9 @@ from pyldl.algorithms.base import BaseLDL +EPS = np.finfo(np.float64).eps + + class _SA(BaseLDL): def __init__(self, random_state=None): @@ -12,8 +15,8 @@ def __init__(self, random_state=None): self._W = None def _loss_function(self, y, y_pred): - y_true = np.clip(y, 1e-7, 1) - y_pred = np.clip(y_pred, 1e-7, 1) + y_true = np.clip(y, EPS, 1) + y_pred = np.clip(y_pred, EPS, 1) return -1 * np.sum(y_true * np.log(y_pred)) def predict(self, X): diff --git a/pyldl/algorithms/base.py b/pyldl/algorithms/base.py index 184e0d1..64ef05d 100644 --- a/pyldl/algorithms/base.py +++ b/pyldl/algorithms/base.py @@ -105,7 +105,7 @@ def fit(self, X, y, **kwargs): elif issubclass(self.__class__, BaseLE): BaseLE.fit(self, X, y, **kwargs) else: - raise ValueError("The model must be a subclass of BaseLDL or BaseLE.") + raise TypeError("The model must be a subclass of BaseLDL or BaseLE.") class BaseADMM(Base): @@ -176,10 +176,12 @@ def __init__(self, n_hidden=64, n_latent=None, random_state=None): @staticmethod @tf.function def _l2_reg(model): - reg = 0. - for i in model.trainable_variables: - reg += tf.reduce_sum(tf.square(i)) - return reg / 2. + if isinstance(model, keras.Model): + return tf.reduce_sum([tf.reduce_sum(tf.square(v)) for v in model.trainable_variables]) / 2. + elif isinstance(model, tf.Tensor): + return tf.reduce_sum(tf.square(model)) / 2. + else: + raise TypeError("Input must be a keras.Model or tf.Tensor.") @staticmethod @tf.function @@ -191,10 +193,9 @@ def _call(self, X): @staticmethod def get_2layer_model(n_features, n_outputs, softmax=True): + activation = 'softmax' if softmax else None return keras.Sequential([keras.layers.InputLayer(input_shape=(n_features,)), - keras.layers.Dense(n_outputs, kernel_initializer=keras.initializers.Zeros(), - activation='softmax' if softmax else None, - use_bias=False)]) + keras.layers.Dense(n_outputs, activation=activation, use_bias=False)]) @staticmethod def get_3layer_model(n_features, n_hidden, n_outputs): @@ -276,7 +277,7 @@ def fit(self, X, y, **kwargs): elif issubclass(self.__class__, BaseDeepLE): BaseDeepLE.fit(self, X, y, **kwargs) else: - raise ValueError("The model must be a subclass of BaseDeepLDL or BaseDeepLE.") + raise TypeError("The model must be a subclass of BaseDeepLDL or BaseDeepLE.") class BaseGD(BaseDeep): diff --git a/pyldl/algorithms/utils.py b/pyldl/algorithms/utils.py new file mode 100644 index 0000000..16bdaed --- /dev/null +++ b/pyldl/algorithms/utils.py @@ -0,0 +1,85 @@ +import numpy as np + +import keras +import tensorflow as tf + +from keras import backend as K + +def pairwise_euclidean(X: tf.Tensor, Y: tf.Tensor = None) -> tf.Tensor: + """Pairwise Euclidean distance. + + :param X: Matrix :math:`\\boldsymbol{X}` (shape: :math:`[m_X,\, n_X]`). + :type X: tf.Tensor + :param Y: Matrix :math:`\\boldsymbol{Y}` (shape: :math:`[m_Y,\, n_Y]`). + :type Y: tf.Tensor + :return: Pairwise Euclidean distance (shape: :math:`[m_X,\, m_Y]`). + :rtype: tf.Tensor + """ + Y = X if Y is None else Y + X2 = tf.tile(tf.reduce_sum(tf.square(X), axis=1, keepdims=True), [1, tf.shape(Y)[0]]) + Y2 = tf.tile(tf.reduce_sum(tf.square(Y), axis=1, keepdims=True), [1, tf.shape(X)[0]]) + XY = tf.matmul(X, tf.transpose(Y)) + return X2 + tf.transpose(Y2) - 2 * XY + + +class RProp(keras.optimizers.Optimizer): + + def __init__(self, init_alpha=1e-3, scale_up=1.2, scale_down=0.5, min_alpha=1e-6, max_alpha=50., **kwargs): + super(RProp, self).__init__(name='rprop', **kwargs) + self.init_alpha = K.variable(init_alpha, name='init_alpha') + self.scale_up = K.variable(scale_up, name='scale_up') + self.scale_down = K.variable(scale_down, name='scale_down') + self.min_alpha = K.variable(min_alpha, name='min_alpha') + self.max_alpha = K.variable(max_alpha, name='max_alpha') + + def apply_gradients(self, grads_and_vars): + grads, trainable_variables = zip(*grads_and_vars) + self.get_updates(trainable_variables, grads) + + def get_updates(self, params, gradients): + grads = gradients + shapes = [K.int_shape(p) for p in params] + alphas = [K.variable(np.ones(shape) * self.init_alpha) for shape in shapes] + old_grads = [K.zeros(shape) for shape in shapes] + prev_weight_deltas = [K.zeros(shape) for shape in shapes] + self.updates = [] + + for param, grad, old_grad, prev_weight_delta, alpha in zip(params, grads, + old_grads, prev_weight_deltas, + alphas): + + new_alpha = K.switch( + K.greater(grad * old_grad, 0), + K.minimum(alpha * self.scale_up, self.max_alpha), + K.switch(K.less(grad * old_grad, 0), K.maximum(alpha * self.scale_down, self.min_alpha), alpha) + ) + + new_delta = K.switch(K.greater(grad, 0), + -new_alpha, + K.switch(K.less(grad, 0), + new_alpha, + K.zeros_like(new_alpha))) + + weight_delta = K.switch(K.less(grad*old_grad, 0), -prev_weight_delta, new_delta) + + new_param = param + weight_delta + + grad = K.switch(K.less(grad*old_grad, 0), K.zeros_like(grad), grad) + + self.updates.append(K.update(param, new_param)) + self.updates.append(K.update(alpha, new_alpha)) + self.updates.append(K.update(old_grad, grad)) + self.updates.append(K.update(prev_weight_delta, weight_delta)) + + return self.updates + + def get_config(self): + config = { + 'init_alpha': float(K.get_value(self.init_alpha)), + 'scale_up': float(K.get_value(self.scale_up)), + 'scale_down': float(K.get_value(self.scale_down)), + 'min_alpha': float(K.get_value(self.min_alpha)), + 'max_alpha': float(K.get_value(self.max_alpha)), + } + base_config = super(RProp, self).get_config() + return dict(list(base_config.items()) + list(config.items())) diff --git a/pyldl/metrics.py b/pyldl/metrics.py index abc29a1..0f79d08 100644 --- a/pyldl/metrics.py +++ b/pyldl/metrics.py @@ -5,7 +5,7 @@ from sklearn.metrics.pairwise import paired_cosine_distances -eps = np.finfo(np.float64).eps +EPS = np.finfo(np.float64).eps THE_SMALLER_THE_BETTER = ["chebyshev", "clark", "canberra", "kl_divergence", @@ -23,8 +23,8 @@ def _clip(func): def _wrapper(y, y_pred): - y = np.clip(y, eps, 1) - y_pred = np.clip(y_pred, eps, 1) + y = np.clip(y, EPS, 1) + y_pred = np.clip(y_pred, EPS, 1) return func(y, y_pred) return _wrapper