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.
- pyldl/algorithms/__init__.py +191 -23
- pyldl/algorithms/_algorithm_adaptation.py +49 -184
- pyldl/algorithms/_bp.py +12 -0
- pyldl/algorithms/_classifier.py +88 -62
- pyldl/algorithms/_cpnn.py +96 -0
- pyldl/algorithms/_delta_ldl.py +172 -0
- pyldl/algorithms/_duo_ldl.py +33 -0
- pyldl/algorithms/_ensemble.py +210 -64
- pyldl/algorithms/_incomplete.py +43 -34
- pyldl/algorithms/_label_enhancement.py +206 -147
- pyldl/algorithms/_ldl_da.py +94 -100
- pyldl/algorithms/_ldl_dpa.py +24 -25
- pyldl/algorithms/_ldl_dpm.py +201 -0
- pyldl/algorithms/_ldl_dvs.py +71 -0
- pyldl/algorithms/_ldl_hvlc.py +65 -0
- pyldl/algorithms/_ldl_lclr.py +167 -0
- pyldl/algorithms/_ldl_lrr.py +25 -24
- pyldl/algorithms/_ldl_scl.py +62 -34
- pyldl/algorithms/_ldlf.py +79 -49
- pyldl/algorithms/_ldllc.py +26 -0
- pyldl/algorithms/_ldlsf.py +101 -0
- pyldl/algorithms/_lrldl.py +105 -0
- pyldl/algorithms/_problem_transformation.py +80 -33
- pyldl/algorithms/_rbm.c +14902 -0
- pyldl/algorithms/_rknn_ldl.py +117 -0
- pyldl/algorithms/_s_ldl.py +220 -0
- pyldl/algorithms/_snefy_ldl.py +91 -0
- pyldl/algorithms/_specialized_algorithms.py +216 -60
- pyldl/algorithms/_ssg_ldl.py +22 -18
- pyldl/algorithms/_tree.c +33826 -0
- pyldl/algorithms/base/__init__.py +42 -0
- pyldl/algorithms/base/_lazy.py +19 -0
- pyldl/algorithms/base/deep.py +398 -0
- pyldl/algorithms/base/shallow.py +533 -0
- pyldl/algorithms/callbacks.py +71 -0
- pyldl/algorithms/loss_function_engineering.py +68 -0
- pyldl/algorithms/optimizers.py +81 -0
- pyldl/algorithms/utils.py +660 -0
- pyldl/applications/emphasis_selection.py +20 -12
- pyldl/applications/facial_emotion_recognition.py +51 -29
- pyldl/applications/lesion_counting.py +54 -36
- pyldl/experiment.py +170 -0
- pyldl/matlab_algorithms/__init__.py +12 -10
- pyldl/metrics.py +502 -70
- pyldl/utils.py +283 -118
- {python_ldl-0.0.3.dist-info → python_ldl-0.1.0.dist-info}/METADATA +103 -45
- python_ldl-0.1.0.dist-info/RECORD +66 -0
- {python_ldl-0.0.3.dist-info → python_ldl-0.1.0.dist-info}/WHEEL +1 -1
- {python_ldl-0.0.3.dist-info → python_ldl-0.1.0.dist-info/licenses}/LICENSE +1 -1
- {python_ldl-0.0.3.dist-info → python_ldl-0.1.0.dist-info}/top_level.txt +1 -0
- tests/__init__.py +0 -0
- tests/test.py +40 -0
- pyldl/algorithms/base.py +0 -357
- python_ldl-0.0.3.dist-info/RECORD +0 -40
pyldl/algorithms/__init__.py
CHANGED
|
@@ -1,30 +1,198 @@
|
|
|
1
|
-
from
|
|
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
|
-
|
|
6
|
-
from .
|
|
7
|
-
from .
|
|
3
|
+
if TYPE_CHECKING:
|
|
4
|
+
from ._problem_transformation import *
|
|
5
|
+
from ._algorithm_adaptation import *
|
|
6
|
+
from ._specialized_algorithms import *
|
|
8
7
|
|
|
9
|
-
from .
|
|
10
|
-
from .
|
|
11
|
-
from .
|
|
12
|
-
from ._ldl_dpa import LDL_DPA
|
|
8
|
+
from ._incomplete import *
|
|
9
|
+
from ._classifier import *
|
|
10
|
+
from ._ensemble import *
|
|
13
11
|
|
|
14
|
-
from .
|
|
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 .
|
|
31
|
+
from ._ssg_ldl import *
|
|
17
32
|
|
|
18
|
-
from .
|
|
33
|
+
from ._label_enhancement import *
|
|
19
34
|
|
|
35
|
+
from ._ldl_da import *
|
|
20
36
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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.
|
|
20
|
-
return np.average(self.
|
|
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
|
|
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
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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)
|
pyldl/algorithms/_bp.py
ADDED
|
@@ -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)
|
pyldl/algorithms/_classifier.py
CHANGED
|
@@ -1,105 +1,131 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
1
3
|
import keras
|
|
2
|
-
import
|
|
4
|
+
import keras.ops as ops
|
|
3
5
|
|
|
4
6
|
from pyldl.algorithms.base import BaseDeepLDLClassifier, BaseGD, BaseBFGS
|
|
5
7
|
|
|
6
8
|
|
|
7
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
12
|
+
class LDL4C(BaseBFGS, BaseDeepLDLClassifier):
|
|
13
|
+
""":class:`LDL4C <pyldl.algorithms.LDL4C>` is proposed in paper :cite:`2019:wang3`.
|
|
14
|
+
"""
|
|
22
15
|
|
|
23
|
-
def
|
|
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
|
-
|
|
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
|
|
40
|
+
""":class:`LDL-HR <pyldl.algorithms.LDL_HR>` is proposed in paper :cite:`2021:wang3`.
|
|
32
41
|
"""
|
|
33
42
|
|
|
34
|
-
|
|
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
|
-
|
|
51
|
+
theta = self._params2model(params_1d)[0]
|
|
52
|
+
D_pred = keras.activations.softmax(self._X @ theta)
|
|
37
53
|
|
|
38
|
-
highest =
|
|
39
|
-
rest =
|
|
40
|
-
margin =
|
|
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 =
|
|
43
|
-
rest_mae =
|
|
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 =
|
|
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(
|
|
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 =
|
|
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.
|
|
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
|
|
77
|
+
""":class:`LDLM <pyldl.algorithms.LDLM>` is proposed in paper :cite:`2021:wang`.
|
|
65
78
|
"""
|
|
66
79
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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 +
|
|
115
|
+
self._beta * second_margin + \
|
|
116
|
+
self._gamma * self._l2_reg(self._model)
|
|
85
117
|
|
|
86
118
|
def _before_train(self):
|
|
87
|
-
temp =
|
|
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
|
-
|
|
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.
|
|
95
|
-
self.
|
|
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)
|