python-ldl 0.0.3__py3-none-any.whl → 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. pyldl/algorithms/__init__.py +191 -23
  2. pyldl/algorithms/_algorithm_adaptation.py +49 -184
  3. pyldl/algorithms/_bp.py +12 -0
  4. pyldl/algorithms/_classifier.py +88 -62
  5. pyldl/algorithms/_cpnn.py +96 -0
  6. pyldl/algorithms/_delta_ldl.py +172 -0
  7. pyldl/algorithms/_duo_ldl.py +33 -0
  8. pyldl/algorithms/_ensemble.py +210 -64
  9. pyldl/algorithms/_incomplete.py +43 -34
  10. pyldl/algorithms/_label_enhancement.py +206 -147
  11. pyldl/algorithms/_ldl_da.py +94 -100
  12. pyldl/algorithms/_ldl_dpa.py +24 -25
  13. pyldl/algorithms/_ldl_dpm.py +201 -0
  14. pyldl/algorithms/_ldl_dvs.py +71 -0
  15. pyldl/algorithms/_ldl_hvlc.py +65 -0
  16. pyldl/algorithms/_ldl_lclr.py +167 -0
  17. pyldl/algorithms/_ldl_lrr.py +25 -24
  18. pyldl/algorithms/_ldl_scl.py +62 -34
  19. pyldl/algorithms/_ldlf.py +79 -49
  20. pyldl/algorithms/_ldllc.py +26 -0
  21. pyldl/algorithms/_ldlsf.py +101 -0
  22. pyldl/algorithms/_lrldl.py +105 -0
  23. pyldl/algorithms/_problem_transformation.py +80 -33
  24. pyldl/algorithms/_rbm.c +14902 -0
  25. pyldl/algorithms/_rknn_ldl.py +117 -0
  26. pyldl/algorithms/_s_ldl.py +220 -0
  27. pyldl/algorithms/_snefy_ldl.py +91 -0
  28. pyldl/algorithms/_specialized_algorithms.py +216 -60
  29. pyldl/algorithms/_ssg_ldl.py +22 -18
  30. pyldl/algorithms/_tree.c +33826 -0
  31. pyldl/algorithms/base/__init__.py +42 -0
  32. pyldl/algorithms/base/_lazy.py +19 -0
  33. pyldl/algorithms/base/deep.py +398 -0
  34. pyldl/algorithms/base/shallow.py +533 -0
  35. pyldl/algorithms/callbacks.py +71 -0
  36. pyldl/algorithms/loss_function_engineering.py +68 -0
  37. pyldl/algorithms/optimizers.py +81 -0
  38. pyldl/algorithms/utils.py +660 -0
  39. pyldl/applications/emphasis_selection.py +20 -12
  40. pyldl/applications/facial_emotion_recognition.py +51 -29
  41. pyldl/applications/lesion_counting.py +54 -36
  42. pyldl/experiment.py +170 -0
  43. pyldl/matlab_algorithms/__init__.py +12 -10
  44. pyldl/metrics.py +502 -70
  45. pyldl/utils.py +283 -118
  46. {python_ldl-0.0.3.dist-info → python_ldl-0.1.0.dist-info}/METADATA +103 -45
  47. python_ldl-0.1.0.dist-info/RECORD +66 -0
  48. {python_ldl-0.0.3.dist-info → python_ldl-0.1.0.dist-info}/WHEEL +1 -1
  49. {python_ldl-0.0.3.dist-info → python_ldl-0.1.0.dist-info/licenses}/LICENSE +1 -1
  50. {python_ldl-0.0.3.dist-info → python_ldl-0.1.0.dist-info}/top_level.txt +1 -0
  51. tests/__init__.py +0 -0
  52. tests/test.py +40 -0
  53. pyldl/algorithms/base.py +0 -357
  54. python_ldl-0.0.3.dist-info/RECORD +0 -40
@@ -1,30 +1,198 @@
1
- from ._problem_transformation import PT_Bayes, PT_SVM, LDSVR
2
- from ._algorithm_adaptation import AA_BP, AA_KNN, CAD, QFD2, CJS, CPNN, BCPNN, ACPNN
3
- from ._specialized_algorithms import SA_BFGS, SA_IIS
1
+ from typing import TYPE_CHECKING
4
2
 
5
- from ._incomplete import IncomLDL
6
- from ._classifier import LDL4C, LDL_HR, LDLM
7
- from ._ensemble import DF_LDL, AdaBoostLDL
3
+ if TYPE_CHECKING:
4
+ from ._problem_transformation import *
5
+ from ._algorithm_adaptation import *
6
+ from ._specialized_algorithms import *
8
7
 
9
- from ._ldlf import LDLF
10
- from ._ldl_scl import LDL_SCL
11
- from ._ldl_lrr import LDL_LRR
12
- from ._ldl_dpa import LDL_DPA
8
+ from ._incomplete import *
9
+ from ._classifier import *
10
+ from ._ensemble import *
13
11
 
14
- from ._ssg_ldl import SSG_LDL
12
+ from ._bp import *
13
+ from ._cpnn import *
14
+ from ._duo_ldl import *
15
+ from ._ldlf import *
16
+ from ._ldllc import *
17
+ from ._ldlsf import *
18
+ from ._ldl_lclr import *
19
+ from ._ldl_scl import *
20
+ from ._ldl_lrr import *
21
+ from ._ldl_dpa import *
22
+ from ._lrldl import *
23
+ from ._ldl_hvlc import *
24
+ from ._rknn_ldl import *
25
+ from ._s_ldl import *
26
+ from ._delta_ldl import *
27
+ from ._snefy_ldl import *
28
+ from ._ldl_dvs import *
29
+ from ._ldl_dpm import *
15
30
 
16
- from ._label_enhancement import FCM, KM, LP, ML, GLLE, LEVI, LIBLE
31
+ from ._ssg_ldl import *
17
32
 
18
- from ._ldl_da import LDL_DA
33
+ from ._label_enhancement import *
19
34
 
35
+ from ._ldl_da import *
20
36
 
21
- __all__ = ["SA_BFGS", "SA_IIS", "AA_KNN", "AA_BP", "PT_Bayes", "PT_SVM",
22
- "CPNN", "BCPNN", "ACPNN", "LDSVR",
23
- "LDLF", "LDL_SCL", "LDL_LRR", "LDL_DPA", "CAD", "QFD2", "CJS",
24
- "DF_LDL", "AdaBoostLDL",
25
- "LDL4C", "LDL_HR", "LDLM",
26
- "IncomLDL",
27
- "SSG_LDL",
28
- "FCM", "KM", "LP", "ML", "GLLE",
29
- "LEVI", "LIBLE",
30
- "LDL_DA"]
37
+
38
+ _LDL_MODULE_MAP = {
39
+ # -------------------- 2026 --------------------
40
+ "LDL_DVS": "._ldl_dvs",
41
+ "LDL_DPM": "._ldl_dpm",
42
+ # -------------------- 2025 --------------------
43
+ "RG4LDL": "._ensemble",
44
+ "RKNN_LDL": "._rknn_ldl",
45
+ "SNEFY_LDL": "._snefy_ldl",
46
+ **dict.fromkeys(
47
+ [
48
+ "_S_LDL",
49
+ "S_LRR",
50
+ "S_SCL",
51
+ "S_KLD",
52
+ "S_CJS",
53
+ "S_QFD2",
54
+ "Shallow_S_LDL",
55
+ ],
56
+ "._s_ldl"
57
+ ),
58
+ "Delta_LDL": "._delta_ldl",
59
+ # -------------------- 2024 --------------------
60
+ "LDL_HVLC": "._ldl_hvlc",
61
+ **dict.fromkeys(
62
+ [
63
+ "_LRLDL",
64
+ "TKLRLDL",
65
+ "TLRLDL",
66
+ ],
67
+ "._lrldl"
68
+ ),
69
+ "LDL_DPA": "._ldl_dpa",
70
+ # -------------------- 2023 --------------------
71
+ "LDL_LRR": "._ldl_lrr",
72
+ # -------------------- 2021 --------------------
73
+ "DF_LDL": "._ensemble",
74
+ "LDL_SCL": "._ldl_scl",
75
+ "Duo_LDL": "._duo_ldl",
76
+ "BD_LDL": "._algorithm_adaptation",
77
+ # -------------------- 2019 --------------------
78
+ "LDL_LCLR": "._ldl_lclr",
79
+ "LDLSF": "._ldlsf",
80
+ # -------------------- 2018 --------------------
81
+ "LDLLC": "._ldllc",
82
+ "LALOT": "._specialized_algorithms",
83
+ "StructRF": "._ensemble",
84
+ # -------------------- 2017 --------------------
85
+ "BCPNN": "._cpnn",
86
+ "ACPNN": "._cpnn",
87
+ "LDLF": "._ldlf",
88
+ # -------------------- 2016 --------------------
89
+ "LDLogitBoost": "._ensemble",
90
+ **dict.fromkeys(
91
+ [
92
+ "_SA",
93
+ "SA_BFGS",
94
+ "SA_IIS",
95
+ ],
96
+ "._specialized_algorithms"
97
+ ),
98
+ "AA_KNN": "._algorithm_adaptation",
99
+ "AA_BP": "._bp",
100
+ **dict.fromkeys(
101
+ [
102
+ "_PT",
103
+ "PT_Bayes",
104
+ "PT_SVM",
105
+ ],
106
+ "._problem_transformation"
107
+ ),
108
+ # -------------------- 2015 --------------------
109
+ "LDSVR": "._problem_transformation",
110
+ # -------------------- 2013 --------------------
111
+ "CPNN": "._cpnn",
112
+ }
113
+
114
+ _LE_MODULE_MAP = {
115
+ # -------------------- 2023 --------------------
116
+ **dict.fromkeys(
117
+ [
118
+ "LIBLE",
119
+ "ConLE",
120
+ ],
121
+ "._label_enhancement"
122
+ ),
123
+ # -------------------- 2020 --------------------
124
+ "LEVI": "._label_enhancement",
125
+ # -------------------- 2019 --------------------
126
+ **dict.fromkeys(
127
+ [
128
+ "GLLE",
129
+ "ML",
130
+ "LP",
131
+ "KM",
132
+ "FCM",
133
+ ],
134
+ "._label_enhancement"
135
+ ),
136
+ }
137
+
138
+ _INCOMLDL_MODULE_MAP = {
139
+ # -------------------- 2024 --------------------
140
+ "WInLDL": "._incomplete",
141
+ # -------------------- 2017 --------------------
142
+ "IncomLDL": "._incomplete",
143
+ }
144
+
145
+ _LDL4C_MODULE_MAP = {
146
+ # -------------------- 2021 --------------------
147
+ **dict.fromkeys(
148
+ [
149
+ "LDLM",
150
+ "LDL_HR",
151
+ "LDL4C",
152
+ ],
153
+ "._classifier"
154
+ ),
155
+ }
156
+
157
+ _SSG_LDL_MODULE_MAP = {
158
+ # -------------------- 2021 --------------------
159
+ "SSG_LDL": "._ssg_ldl"
160
+ }
161
+
162
+ _LDL_DA_MODULE_MAP = {
163
+ # -------------------- 2025 --------------------
164
+ "LDL_DA": "._ldl_da"
165
+ }
166
+
167
+ _GLD_MODULE_MAP = {
168
+ # -------------------- 2026 --------------------
169
+ "GLD_SVR": "._problem_transformation",
170
+ "GLD_KNN": "._algorithm_adaptation",
171
+ "GLD_BFGS": "._specialized_algorithms",
172
+ }
173
+
174
+ _MODULE_MAP = {
175
+ **_LDL_MODULE_MAP,
176
+ **_LE_MODULE_MAP,
177
+ **_INCOMLDL_MODULE_MAP,
178
+ **_LDL4C_MODULE_MAP,
179
+ **_SSG_LDL_MODULE_MAP,
180
+ **_LDL_DA_MODULE_MAP,
181
+ **_GLD_MODULE_MAP,
182
+ }
183
+
184
+
185
+ _ldl__ = list(_LDL_MODULE_MAP)
186
+ _le__ = list(_LE_MODULE_MAP)
187
+ _incomldl__ = list(_INCOMLDL_MODULE_MAP)
188
+ _ldl4c__ = list(_LDL4C_MODULE_MAP)
189
+ _ssg_ldl__ = list(_SSG_LDL_MODULE_MAP)
190
+ _ldl_da__ = list(_LDL_DA_MODULE_MAP)
191
+ _gld__ = list(_GLD_MODULE_MAP)
192
+
193
+
194
+ __all__ = _ldl__ + _le__ + _incomldl__ + _ldl4c__ + _ssg_ldl__ + _ldl_da__ + _gld__
195
+
196
+
197
+ from .base._lazy import lazy_module
198
+ __getattr__ = lazy_module(_MODULE_MAP, __name__)
@@ -1,198 +1,63 @@
1
1
  import numpy as np
2
- from sklearn.neighbors import NearestNeighbors
3
2
 
4
- import keras
5
- import tensorflow as tf
6
- from keras import backend as K
3
+ from pyldl.algorithms.base import Base, BaseLDL, BaseGLD
7
4
 
8
- from pyldl.algorithms.base import BaseLDL, BaseDeepLDL, BaseGD, BaseAdam
9
5
 
6
+ EPS = np.finfo(np.float32).eps
10
7
 
11
- class AA_KNN(BaseLDL):
12
8
 
13
- def fit(self, X, y, k=5):
14
- super().fit(X, y)
15
- self._model = NearestNeighbors(n_neighbors=k).fit(self._X)
9
+ class _AA_KNN(Base):
10
+ def __init__(self, *, k: int = 5, **kwargs):
11
+ super().__init__(**kwargs)
12
+ self.k = k
13
+
14
+ def fit(self, X: np.ndarray, target: np.ndarray):
15
+ from sklearn.neighbors import NearestNeighbors
16
+ super().fit(X, target)
17
+ self._knn = NearestNeighbors(n_neighbors=self.k).fit(self._X)
16
18
  return self
17
19
 
18
20
  def predict(self, X):
19
- _, inds = self._model.kneighbors(X)
20
- return np.average(self._y[inds], axis=1)
21
+ _, inds = self._knn.kneighbors(X)
22
+ return np.average(self._target[inds], axis=1)
23
+
24
+ def __getstate__(self):
25
+ return self.__dict__.copy()
26
+
27
+ def __setstate__(self, state):
28
+ self.__dict__.update(state)
29
+
30
+
31
+ class AA_KNN(_AA_KNN, BaseLDL):
32
+ """:class:`AA-kNN <pyldl.algorithms.AA_KNN>` is proposed in paper :cite:`2016:geng`.
33
+ """
34
+ pass
21
35
 
22
36
 
23
- class AA_BP(BaseGD, BaseDeepLDL):
37
+ class GLD_KNN(_AA_KNN, BaseGLD):
38
+ """:class:`GLD-kNN <pyldl.algorithms.GLD_KNN>` is proposed in paper :cite:`2026:wu`.
39
+ """
24
40
  pass
25
41
 
26
42
 
27
- class CAD(BaseAdam, BaseDeepLDL):
28
-
29
- @staticmethod
30
- @tf.function
31
- def loss_function(y, y_pred):
32
- def _CAD(y, y_pred):
33
- return tf.reduce_mean(tf.abs(
34
- tf.cumsum(y, axis=1) - tf.cumsum(y_pred, axis=1)
35
- ), axis=1)
36
- return tf.math.reduce_sum(
37
- tf.map_fn(lambda i: _CAD(y[:, :i], y_pred[:, :i]),
38
- tf.range(1, y.shape[1] + 1),
39
- fn_output_signature=tf.float32)
40
- )
41
-
42
-
43
- class QFD2(BaseAdam, BaseDeepLDL):
44
-
45
- @staticmethod
46
- @tf.function
47
- def _loss_function(y, y_pred):
48
- Q = y - y_pred
49
- j = tf.reshape(tf.range(y.shape[1]), [y.shape[1], 1])
50
- k = tf.reshape(tf.range(y.shape[1]), [1, y.shape[1]])
51
- A = tf.cast(1 - tf.abs(j - k) / (y.shape[1] - 1), dtype=tf.float32)
52
- return tf.math.reduce_mean(
53
- tf.linalg.diag_part(tf.matmul(tf.matmul(Q, A), tf.transpose(Q)))
54
- )
55
-
56
-
57
- class CJS(BaseAdam, BaseDeepLDL):
58
-
59
- @staticmethod
60
- @tf.function
61
- def loss_function(y, y_pred):
62
- def _CJS(y, y_pred):
63
- m = 0.5 * (y + y_pred)
64
- js = 0.5 * (keras.losses.kl_divergence(y, m) + keras.losses.kl_divergence(y_pred, m))
65
- return tf.reduce_mean(js)
66
- return tf.math.reduce_sum(
67
- tf.map_fn(lambda i: _CJS(y[:, :i], y_pred[:, :i]),
68
- tf.range(1, y.shape[1] + 1),
69
- fn_output_signature=tf.float32)
70
- )
71
-
72
-
73
- class RProp(keras.optimizers.Optimizer):
74
-
75
- def __init__(self, init_alpha=1e-3, scale_up=1.2, scale_down=0.5, min_alpha=1e-6, max_alpha=50., **kwargs):
76
- super(RProp, self).__init__(name='rprop', **kwargs)
77
- self.init_alpha = K.variable(init_alpha, name='init_alpha')
78
- self.scale_up = K.variable(scale_up, name='scale_up')
79
- self.scale_down = K.variable(scale_down, name='scale_down')
80
- self.min_alpha = K.variable(min_alpha, name='min_alpha')
81
- self.max_alpha = K.variable(max_alpha, name='max_alpha')
82
-
83
- def apply_gradients(self, grads_and_vars):
84
- grads, trainable_variables = zip(*grads_and_vars)
85
- self.get_updates(trainable_variables, grads)
86
-
87
- def get_updates(self, params, gradients):
88
- grads = gradients
89
- shapes = [K.int_shape(p) for p in params]
90
- alphas = [K.variable(np.ones(shape) * self.init_alpha) for shape in shapes]
91
- old_grads = [K.zeros(shape) for shape in shapes]
92
- prev_weight_deltas = [K.zeros(shape) for shape in shapes]
93
- self.updates = []
94
-
95
- for param, grad, old_grad, prev_weight_delta, alpha in zip(params, grads,
96
- old_grads, prev_weight_deltas,
97
- alphas):
98
-
99
- new_alpha = K.switch(
100
- K.greater(grad * old_grad, 0),
101
- K.minimum(alpha * self.scale_up, self.max_alpha),
102
- K.switch(K.less(grad * old_grad, 0), K.maximum(alpha * self.scale_down, self.min_alpha), alpha)
103
- )
104
-
105
- new_delta = K.switch(K.greater(grad, 0),
106
- -new_alpha,
107
- K.switch(K.less(grad, 0),
108
- new_alpha,
109
- K.zeros_like(new_alpha)))
110
-
111
- weight_delta = K.switch(K.less(grad*old_grad, 0), -prev_weight_delta, new_delta)
112
-
113
- new_param = param + weight_delta
114
-
115
- grad = K.switch(K.less(grad*old_grad, 0), K.zeros_like(grad), grad)
116
-
117
- self.updates.append(K.update(param, new_param))
118
- self.updates.append(K.update(alpha, new_alpha))
119
- self.updates.append(K.update(old_grad, grad))
120
- self.updates.append(K.update(prev_weight_delta, weight_delta))
121
-
122
- return self.updates
123
-
124
- def get_config(self):
125
- config = {
126
- 'init_alpha': float(K.get_value(self.init_alpha)),
127
- 'scale_up': float(K.get_value(self.scale_up)),
128
- 'scale_down': float(K.get_value(self.scale_down)),
129
- 'min_alpha': float(K.get_value(self.min_alpha)),
130
- 'max_alpha': float(K.get_value(self.max_alpha)),
131
- }
132
- base_config = super(RProp, self).get_config()
133
- return dict(list(base_config.items()) + list(config.items()))
134
-
135
-
136
- class CPNN(BaseGD, BaseDeepLDL):
137
-
138
- def _not_proper_mode(self):
139
- raise ValueError("The argument 'mode' can only be 'none', 'binary' or 'augment'.")
140
-
141
- def __init__(self, mode='none', v=5, n_hidden=64, n_latent=None, random_state=None):
142
- super().__init__(n_hidden, n_latent, random_state)
143
- if mode == 'none' or mode == 'binary' or mode == 'augment':
144
- self._mode = mode
145
- else:
146
- self._not_proper_mode()
147
- self._v = v
148
-
149
- @staticmethod
150
- @tf.function
151
- def loss_function(y, y_pred):
152
- return tf.math.reduce_mean(keras.losses.kl_divergence(y, y_pred))
153
-
154
- def _get_default_model(self):
155
- input_shape = (self._n_features + (1 if self._mode == 'none' else self._n_outputs),)
156
- return keras.Sequential([keras.layers.InputLayer(input_shape=input_shape),
157
- keras.layers.Dense(self._n_hidden, activation='sigmoid'),
158
- keras.layers.Dense(1, activation=None)])
159
-
160
- def _get_default_optimizer(self):
161
- return RProp()
162
-
163
- def _before_train(self):
164
- if self._mode == 'augment':
165
- n = self._X.shape[0]
166
- one_hot = tf.one_hot(tf.math.argmax(self._y, axis=1), self.n_outputs)
167
- self._X = tf.repeat(self._X, self._v, axis=0)
168
- self._y = tf.repeat(self._y, self._v, axis=0)
169
- one_hot = tf.repeat(one_hot, self._v, axis=0)
170
- v = tf.reshape(tf.tile([1 / (i + 1) for i in range(self._v)], [n]), (-1, 1))
171
- self._y += self._y * one_hot * v
172
-
173
- def _make_inputs(self, X):
174
- temp = tf.reshape(tf.tile([i + 1 for i in range(self._n_outputs)], [X.shape[0]]), (-1, 1))
175
- if self._mode != 'none':
176
- temp = tf.one_hot(tf.reshape(temp, (-1, )) - 1, depth=self._n_outputs)
177
- return tf.concat([tf.cast(tf.repeat(X, self._n_outputs, axis=0), dtype=tf.float32),
178
- tf.cast(temp, dtype=tf.float32)],
179
- axis=1)
180
-
181
- def _call(self, X):
182
- inputs = self._make_inputs(X)
183
- outputs = self._model(inputs)
184
- results = tf.reshape(outputs, (X.shape[0], self._n_outputs))
185
- b = tf.reshape(-tf.math.log(tf.math.reduce_sum(tf.math.exp(results), axis=1)), (-1, 1))
186
- return tf.math.exp(b + results)
187
-
188
-
189
- class BCPNN(CPNN):
190
-
191
- def __init__(self, **params):
192
- super().__init__(mode='binary', **params)
193
-
194
-
195
- class ACPNN(CPNN):
196
-
197
- def __init__(self, **params):
198
- super().__init__(mode='augment', **params)
43
+ class BD_LDL(BaseLDL):
44
+ """:class:`BD-LDL <pyldl.algorithms.BD_LDL>` is proposed in paper :cite:`2021:liu2`.
45
+ """
46
+
47
+ def __init__(self, *, alpha: float = 1e-3, beta: float = 1e-2, **kwargs):
48
+ super().__init__(**kwargs)
49
+ self.alpha = alpha
50
+ self.beta = beta
51
+
52
+ def fit(self, X, D):
53
+ from scipy.linalg import solve_sylvester
54
+ super().fit(X, D)
55
+ A = self._X.T @ self._X + self.beta * np.eye(self._n_features)
56
+ B = self.alpha * self._D.T @ self._D
57
+ C = (1 + self.alpha) * self._X.T @ self._D
58
+ self._W = solve_sylvester(A, B, C)
59
+ return self
60
+
61
+ def predict(self, X):
62
+ from pyldl.algorithms.utils import proj
63
+ return proj(X @ self._W)
@@ -0,0 +1,12 @@
1
+ import keras
2
+
3
+ from pyldl.algorithms.base import BaseDeepLDL, BaseGD
4
+
5
+
6
+ @keras.saving.register_keras_serializable()
7
+ class AA_BP(BaseGD, BaseDeepLDL):
8
+ """:class:`AA-BP <pyldl.algorithms.AA_BP>` is proposed in paper :cite:`2016:geng`.
9
+ """
10
+
11
+ def __init__(self, **kwargs):
12
+ super().__init__(**kwargs)
@@ -1,105 +1,131 @@
1
+ import numpy as np
2
+
1
3
  import keras
2
- import tensorflow as tf
4
+ import keras.ops as ops
3
5
 
4
6
  from pyldl.algorithms.base import BaseDeepLDLClassifier, BaseGD, BaseBFGS
5
7
 
6
8
 
7
- class LDL4C(BaseBFGS, BaseDeepLDLClassifier):
8
- """LDL4C is proposed in paper Classification with Label Distribution Learning.
9
- """
9
+ EPS = np.finfo(np.float32).eps
10
10
 
11
- @tf.function
12
- def _loss(self, params_1d):
13
- y_pred = keras.activations.softmax(self._X @ self._params2model(params_1d)[0])
14
- top2 = tf.gather(y_pred, self._top2, axis=1, batch_dims=1)
15
- margin = tf.reduce_sum(tf.maximum(0., 1. - (top2[:, 0] - top2[:, 1]) / self._rho))
16
- mae = keras.losses.mean_absolute_error(self._y, y_pred)
17
- return tf.reduce_sum(self._entropy * mae) + self._alpha * margin + self._beta * self._l2_reg(self._model)
18
11
 
19
- def _before_train(self):
20
- self._top2 = tf.math.top_k(self._y, k=2)[1]
21
- self._entropy = tf.cast(-tf.reduce_sum(self._y * tf.math.log(self._y) + 1e-7, axis=1), dtype=tf.float32)
12
+ class LDL4C(BaseBFGS, BaseDeepLDLClassifier):
13
+ """:class:`LDL4C <pyldl.algorithms.LDL4C>` is proposed in paper :cite:`2019:wang3`.
14
+ """
22
15
 
23
- def fit(self, X, y, alpha=1e-2, beta=1e-6, rho=1e-2, **kwargs):
16
+ def __init__(self, alpha=1e-2, beta=0., rho=1e-2, **kwargs):
17
+ super().__init__(**kwargs)
24
18
  self._alpha = alpha
25
19
  self._beta = beta
26
20
  self._rho = rho
27
- return super().fit(X, y, **kwargs)
21
+
22
+ def _loss(self, params_1d):
23
+ theta = self._params2model(params_1d)[0]
24
+ D_pred = keras.activations.softmax(self._X @ theta)
25
+ top2 = ops.take_along_axis(D_pred, self._top2, axis=1)
26
+ margin = ops.sum(
27
+ ops.maximum(0., 1. - (top2[:, 0] - top2[:, 1]) / self._rho)
28
+ )
29
+ mae = keras.losses.mean_absolute_error(self._D, D_pred)
30
+ return ops.sum(self._entropy * mae) + self._alpha * margin + self._beta * self._l2_reg(theta)
31
+
32
+ def _before_train(self):
33
+ _, self._top2 = ops.top_k(self._D, k=2)
34
+ self._entropy = ops.cast(
35
+ -ops.sum(self._D * ops.log(self._D + EPS), axis=1),
36
+ dtype="float32")
28
37
 
29
38
 
30
39
  class LDL_HR(BaseBFGS, BaseDeepLDLClassifier):
31
- """LDL-HR is proposed in paper Learn the Highest Label and Rest Label Description Degrees.
40
+ """:class:`LDL-HR <pyldl.algorithms.LDL_HR>` is proposed in paper :cite:`2021:wang3`.
32
41
  """
33
42
 
34
- @tf.function
43
+ def __init__(self, alpha=1e-2, beta=1e-2, gamma=0., rho=1e-2, **kwargs):
44
+ super().__init__(**kwargs)
45
+ self._alpha = alpha
46
+ self._beta = beta
47
+ self._gamma = gamma
48
+ self._rho = rho
49
+
35
50
  def _loss(self, params_1d):
36
- y_pred = keras.activations.softmax(self._X @ self._params2model(params_1d)[0])
51
+ theta = self._params2model(params_1d)[0]
52
+ D_pred = keras.activations.softmax(self._X @ theta)
37
53
 
38
- highest = tf.gather(y_pred, self._highest, axis=1, batch_dims=1)
39
- rest = tf.gather(y_pred, self._rest, axis=1, batch_dims=1)
40
- margin = tf.reduce_sum(tf.maximum(0., 1. - (highest - rest) / self._rho))
54
+ highest = ops.take_along_axis(D_pred, self._highest, axis=1)
55
+ rest = ops.take_along_axis(D_pred, self._rest, axis=1)
56
+ margin = ops.sum(
57
+ ops.maximum(0., 1. - (highest - rest) / self._rho)
58
+ )
41
59
 
42
- real_rest = tf.gather(self._y, self._rest, axis=1, batch_dims=1)
43
- rest_mae = tf.reduce_sum(keras.losses.mean_absolute_error(real_rest, rest))
60
+ real_rest = ops.take_along_axis(self._D, self._rest, axis=1)
61
+ rest_mae = ops.sum(
62
+ keras.losses.mean_absolute_error(real_rest, rest)
63
+ )
44
64
 
45
- mae = tf.reduce_sum(keras.losses.mean_absolute_error(self._l, y_pred))
65
+ mae = ops.sum(keras.losses.mean_absolute_error(self._L, D_pred))
46
66
 
47
- return mae + self._alpha * margin + self._beta * rest_mae + self._gamma * self._l2_reg(self._model)
67
+ return mae + self._alpha * margin + self._beta * rest_mae + self._gamma * self._l2_reg(theta)
48
68
 
49
69
  def _before_train(self):
50
- temp = tf.math.top_k(self._y, k=self._n_outputs)[1]
70
+ _, temp = ops.top_k(self._D, k=self._n_outputs)
51
71
  self._highest = temp[:, 0:1]
52
72
  self._rest = temp[:, 1:]
53
- self._l = tf.one_hot(tf.reshape(self._highest, -1), self._n_outputs)
54
-
55
- def fit(self, X, y, alpha=1e-2, beta=1e-2, gamma=1e-6, rho=1e-2, **kwargs):
56
- self._alpha = alpha
57
- self._beta = beta
58
- self._gamma = gamma
59
- self._rho = rho
60
- return super().fit(X, y, **kwargs)
73
+ self._L = ops.one_hot(ops.reshape(self._highest, (-1,)), self._n_outputs)
61
74
 
62
75
 
63
76
  class LDLM(BaseGD, BaseDeepLDLClassifier):
64
- """LDLM is proposed in paper Label Distribution Learning Machine.
77
+ """:class:`LDLM <pyldl.algorithms.LDLM>` is proposed in paper :cite:`2021:wang`.
65
78
  """
66
79
 
67
- @tf.function
68
- def _loss(self, X, y, start, end):
69
- y_pred = self._model(X)
70
-
71
- pred_margin = tf.reduce_sum(tf.clip_by_value(
72
- tf.reduce_sum(tf.abs(self._l[start:end] - y_pred), axis=1) - self._rho,
73
- 0., float('inf')))
74
-
75
- highest = tf.gather(y_pred, self._highest[start:end], axis=1, batch_dims=1)
76
- rest = tf.gather(y_pred, self._rest[start:end], axis=1, batch_dims=1)
77
- label_margin = tf.reduce_sum(tf.maximum(0., 1. - (highest - rest) / self._rho))
80
+ def __init__(self, alpha=1e-2, beta=1e-2, gamma=0., rho=1e-2, **kwargs):
81
+ super().__init__(**kwargs)
82
+ self._alpha = alpha
83
+ self._beta = beta
84
+ self._gamma = gamma
85
+ self._rho = rho
78
86
 
79
- second_margin = tf.reduce_sum(tf.clip_by_value(
80
- tf.reduce_sum(tf.abs((y - y_pred) * self._neg_l[start:end]), axis=1) - self._second_margin[start:end],
81
- 0., float('inf')))
87
+ def _loss(self, X, D, start, end):
88
+ D_pred = self._model(X)
89
+
90
+ pred_margin = ops.sum(
91
+ ops.clip(
92
+ ops.sum(
93
+ ops.abs(self._L[start:end] - D_pred), axis=1
94
+ ) - self._rho, 0., float('inf')
95
+ )
96
+ )
97
+
98
+ highest = ops.take_along_axis(
99
+ D_pred, self._highest[start:end], axis=1
100
+ )
101
+ rest = ops.take_along_axis(D_pred, self._rest[start:end], axis=1)
102
+ label_margin = ops.sum(
103
+ ops.maximum(0., 1. - (highest - rest) / self._rho)
104
+ )
105
+
106
+ second_margin = ops.sum(
107
+ ops.clip(
108
+ ops.sum(
109
+ ops.abs((D - D_pred) * self._neg_L[start:end]), axis=1
110
+ ) - self._second_margin[start:end], 0., float('inf')
111
+ )
112
+ )
82
113
 
83
114
  return pred_margin + self._alpha * label_margin + \
84
- self._beta * second_margin + self._gamma * self._l2_reg(self._model)
115
+ self._beta * second_margin + \
116
+ self._gamma * self._l2_reg(self._model)
85
117
 
86
118
  def _before_train(self):
87
- temp = tf.math.top_k(self._y, k=self._n_outputs)[1]
119
+ _, temp = ops.top_k(self._D, k=self._n_outputs)
88
120
  self._highest = temp[:, 0:1]
89
121
  self._rest = temp[:, 1:]
90
122
 
91
- temp = tf.math.top_k(tf.gather(self._y, self._rest, axis=1, batch_dims=1), k=2)[0]
123
+ rest = ops.take_along_axis(self._D, self._rest, axis=1)
124
+ temp, _ = ops.top_k(rest, k=2)
92
125
  self._second_margin = temp[:, 0] - temp[:, 1]
93
126
 
94
- self._l = tf.one_hot(tf.reshape(self._highest, -1), self._n_outputs)
95
- self._neg_l = tf.where(tf.equal(self._l, 0.), 1., 0.)
127
+ self._L = ops.one_hot(ops.reshape(self._highest, (-1,)), self._n_outputs)
128
+ self._neg_L = ops.where(ops.equal(self._L, 0.), 1., 0.)
96
129
 
97
130
  def _get_default_model(self):
98
131
  return self.get_2layer_model(self._n_features, self._n_outputs)
99
-
100
- def fit(self, X, y, alpha=1e-2, beta=1e-2, gamma=1e-6, rho=1e-2, **kwargs):
101
- self._alpha = alpha
102
- self._beta = beta
103
- self._gamma = gamma
104
- self._rho = rho
105
- return super().fit(X, y, **kwargs)