python-ldl 0.0.2__py3-none-any.whl → 0.0.3__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 +6 -2
- pyldl/algorithms/_algorithm_adaptation.py +41 -100
- pyldl/algorithms/_classifier.py +48 -76
- pyldl/algorithms/_incomplete.py +47 -34
- pyldl/algorithms/_label_enhancement.py +109 -152
- pyldl/algorithms/_ldl_da.py +182 -0
- pyldl/algorithms/_ldl_dpa.py +40 -0
- pyldl/algorithms/_ldl_lrr.py +13 -23
- pyldl/algorithms/_ldl_scl.py +19 -32
- pyldl/algorithms/_ldlf.py +31 -36
- pyldl/algorithms/_problem_transformation.py +15 -23
- pyldl/algorithms/base.py +220 -89
- pyldl/applications/emphasis_selection.py +26 -27
- pyldl/applications/facial_emotion_recognition.py +159 -10
- pyldl/applications/lesion_counting.py +39 -46
- pyldl/metrics.py +66 -21
- pyldl/utils.py +45 -1
- {python_ldl-0.0.2.dist-info → python_ldl-0.0.3.dist-info}/METADATA +7 -7
- python_ldl-0.0.3.dist-info/RECORD +40 -0
- {python_ldl-0.0.2.dist-info → python_ldl-0.0.3.dist-info}/WHEEL +1 -1
- python_ldl-0.0.2.dist-info/RECORD +0 -38
- {python_ldl-0.0.2.dist-info → python_ldl-0.0.3.dist-info}/LICENSE +0 -0
- {python_ldl-0.0.2.dist-info → python_ldl-0.0.3.dist-info}/top_level.txt +0 -0
pyldl/algorithms/__init__.py
CHANGED
|
@@ -9,18 +9,22 @@ from ._ensemble import DF_LDL, AdaBoostLDL
|
|
|
9
9
|
from ._ldlf import LDLF
|
|
10
10
|
from ._ldl_scl import LDL_SCL
|
|
11
11
|
from ._ldl_lrr import LDL_LRR
|
|
12
|
+
from ._ldl_dpa import LDL_DPA
|
|
12
13
|
|
|
13
14
|
from ._ssg_ldl import SSG_LDL
|
|
14
15
|
|
|
15
16
|
from ._label_enhancement import FCM, KM, LP, ML, GLLE, LEVI, LIBLE
|
|
16
17
|
|
|
18
|
+
from ._ldl_da import LDL_DA
|
|
19
|
+
|
|
17
20
|
|
|
18
21
|
__all__ = ["SA_BFGS", "SA_IIS", "AA_KNN", "AA_BP", "PT_Bayes", "PT_SVM",
|
|
19
22
|
"CPNN", "BCPNN", "ACPNN", "LDSVR",
|
|
20
|
-
"LDLF", "LDL_SCL", "LDL_LRR", "CAD", "QFD2", "CJS",
|
|
23
|
+
"LDLF", "LDL_SCL", "LDL_LRR", "LDL_DPA", "CAD", "QFD2", "CJS",
|
|
21
24
|
"DF_LDL", "AdaBoostLDL",
|
|
22
25
|
"LDL4C", "LDL_HR", "LDLM",
|
|
23
26
|
"IncomLDL",
|
|
24
27
|
"SSG_LDL",
|
|
25
28
|
"FCM", "KM", "LP", "ML", "GLLE",
|
|
26
|
-
"LEVI",
|
|
29
|
+
"LEVI", "LIBLE",
|
|
30
|
+
"LDL_DA"]
|
|
@@ -1,128 +1,74 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
from sklearn.neighbors import NearestNeighbors
|
|
3
|
-
from sklearn.preprocessing import OneHotEncoder
|
|
4
3
|
|
|
5
4
|
import keras
|
|
6
5
|
import tensorflow as tf
|
|
7
6
|
from keras import backend as K
|
|
8
7
|
|
|
9
|
-
from pyldl.algorithms.base import BaseLDL, BaseDeepLDL
|
|
8
|
+
from pyldl.algorithms.base import BaseLDL, BaseDeepLDL, BaseGD, BaseAdam
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
class AA_KNN(BaseLDL):
|
|
13
12
|
|
|
14
|
-
def
|
|
15
|
-
k=5,
|
|
16
|
-
random_state=None):
|
|
17
|
-
|
|
18
|
-
super().__init__(random_state)
|
|
19
|
-
|
|
20
|
-
self.k = k
|
|
21
|
-
self._model = NearestNeighbors(n_neighbors=self.k)
|
|
22
|
-
|
|
23
|
-
def fit(self, X, y):
|
|
13
|
+
def fit(self, X, y, k=5):
|
|
24
14
|
super().fit(X, y)
|
|
25
|
-
self._model.fit(self._X)
|
|
15
|
+
self._model = NearestNeighbors(n_neighbors=k).fit(self._X)
|
|
16
|
+
return self
|
|
26
17
|
|
|
27
18
|
def predict(self, X):
|
|
28
19
|
_, inds = self._model.kneighbors(X)
|
|
29
20
|
return np.average(self._y[inds], axis=1)
|
|
30
21
|
|
|
31
22
|
|
|
32
|
-
class AA_BP(BaseDeepLDL):
|
|
23
|
+
class AA_BP(BaseGD, BaseDeepLDL):
|
|
24
|
+
pass
|
|
33
25
|
|
|
34
|
-
def __init__(self, n_hidden=None, n_latent=None, random_state=None):
|
|
35
|
-
super().__init__(n_hidden, n_latent, random_state)
|
|
36
26
|
|
|
37
|
-
|
|
38
|
-
def _loss_function(self, y, y_pred):
|
|
39
|
-
return tf.math.reduce_mean(keras.losses.mean_squared_error(y, y_pred))
|
|
40
|
-
|
|
41
|
-
def fit(self, X, y, learning_rate=5e-3, epochs=3000, batch_size=32,
|
|
42
|
-
model=None, activation='sigmoid', optimizer='SGD', X_test=None, y_test=None):
|
|
43
|
-
super().fit(X, y)
|
|
44
|
-
|
|
45
|
-
self._batch_size = batch_size
|
|
46
|
-
|
|
47
|
-
if self._n_hidden is None:
|
|
48
|
-
self._n_hidden = self._n_features * 3 // 2
|
|
49
|
-
|
|
50
|
-
self._model = model
|
|
51
|
-
if self._model is None:
|
|
52
|
-
self._model = keras.Sequential([keras.layers.InputLayer(input_shape=(self._n_features,)),
|
|
53
|
-
keras.layers.Dense(self._n_hidden, activation=activation),
|
|
54
|
-
keras.layers.Dense(self._n_outputs, activation='softmax')])
|
|
55
|
-
self._optimizer = eval(f'keras.optimizers.{optimizer}({learning_rate})')
|
|
56
|
-
data = tf.data.Dataset.from_tensor_slices((self._X, self._y)).batch(self._batch_size)
|
|
57
|
-
|
|
58
|
-
for _ in range(epochs):
|
|
59
|
-
total_loss = 0.
|
|
60
|
-
for batch in data:
|
|
61
|
-
with tf.GradientTape() as tape:
|
|
62
|
-
y_pred = self._model(batch[0])
|
|
63
|
-
loss = self._loss_function(batch[1], y_pred)
|
|
64
|
-
gradients = tape.gradient(loss, self.trainable_variables)
|
|
65
|
-
self._optimizer.apply_gradients(zip(gradients, self.trainable_variables))
|
|
66
|
-
total_loss += loss
|
|
67
|
-
|
|
68
|
-
def predict(self, X):
|
|
69
|
-
return self._model(X)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
class CAD(AA_BP):
|
|
27
|
+
class CAD(BaseAdam, BaseDeepLDL):
|
|
73
28
|
|
|
29
|
+
@staticmethod
|
|
74
30
|
@tf.function
|
|
75
|
-
def
|
|
31
|
+
def loss_function(y, y_pred):
|
|
76
32
|
def _CAD(y, y_pred):
|
|
77
33
|
return tf.reduce_mean(tf.abs(
|
|
78
34
|
tf.cumsum(y, axis=1) - tf.cumsum(y_pred, axis=1)
|
|
79
35
|
), axis=1)
|
|
80
36
|
return tf.math.reduce_sum(
|
|
81
37
|
tf.map_fn(lambda i: _CAD(y[:, :i], y_pred[:, :i]),
|
|
82
|
-
tf.range(1,
|
|
38
|
+
tf.range(1, y.shape[1] + 1),
|
|
83
39
|
fn_output_signature=tf.float32)
|
|
84
40
|
)
|
|
85
41
|
|
|
86
|
-
def fit(self, X, y, learning_rate=1e-4, epochs=500,
|
|
87
|
-
activation='relu', optimizer='Adam'):
|
|
88
|
-
return super().fit(X, y, learning_rate, epochs, activation, optimizer)
|
|
89
|
-
|
|
90
42
|
|
|
91
|
-
class QFD2(
|
|
43
|
+
class QFD2(BaseAdam, BaseDeepLDL):
|
|
92
44
|
|
|
45
|
+
@staticmethod
|
|
93
46
|
@tf.function
|
|
94
|
-
def _loss_function(
|
|
47
|
+
def _loss_function(y, y_pred):
|
|
95
48
|
Q = y - y_pred
|
|
96
|
-
j = tf.reshape(tf.range(
|
|
97
|
-
k = tf.reshape(tf.range(
|
|
98
|
-
A = tf.cast(1 - tf.abs(j - k) / (
|
|
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)
|
|
99
52
|
return tf.math.reduce_mean(
|
|
100
53
|
tf.linalg.diag_part(tf.matmul(tf.matmul(Q, A), tf.transpose(Q)))
|
|
101
54
|
)
|
|
102
55
|
|
|
103
|
-
def fit(self, X, y, learning_rate=1e-4, epochs=500,
|
|
104
|
-
activation='relu', optimizer='Adam'):
|
|
105
|
-
return super().fit(X, y, learning_rate, epochs, activation, optimizer)
|
|
106
56
|
|
|
57
|
+
class CJS(BaseAdam, BaseDeepLDL):
|
|
107
58
|
|
|
108
|
-
|
|
109
|
-
|
|
59
|
+
@staticmethod
|
|
110
60
|
@tf.function
|
|
111
|
-
def
|
|
61
|
+
def loss_function(y, y_pred):
|
|
112
62
|
def _CJS(y, y_pred):
|
|
113
63
|
m = 0.5 * (y + y_pred)
|
|
114
64
|
js = 0.5 * (keras.losses.kl_divergence(y, m) + keras.losses.kl_divergence(y_pred, m))
|
|
115
65
|
return tf.reduce_mean(js)
|
|
116
66
|
return tf.math.reduce_sum(
|
|
117
67
|
tf.map_fn(lambda i: _CJS(y[:, :i], y_pred[:, :i]),
|
|
118
|
-
tf.range(1,
|
|
68
|
+
tf.range(1, y.shape[1] + 1),
|
|
119
69
|
fn_output_signature=tf.float32)
|
|
120
70
|
)
|
|
121
71
|
|
|
122
|
-
def fit(self, X, y, learning_rate=1e-4, epochs=500,
|
|
123
|
-
activation='relu', optimizer='Adam'):
|
|
124
|
-
return super().fit(X, y, learning_rate, epochs, activation, optimizer)
|
|
125
|
-
|
|
126
72
|
|
|
127
73
|
class RProp(keras.optimizers.Optimizer):
|
|
128
74
|
|
|
@@ -134,6 +80,10 @@ class RProp(keras.optimizers.Optimizer):
|
|
|
134
80
|
self.min_alpha = K.variable(min_alpha, name='min_alpha')
|
|
135
81
|
self.max_alpha = K.variable(max_alpha, name='max_alpha')
|
|
136
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
|
+
|
|
137
87
|
def get_updates(self, params, gradients):
|
|
138
88
|
grads = gradients
|
|
139
89
|
shapes = [K.int_shape(p) for p in params]
|
|
@@ -183,12 +133,12 @@ class RProp(keras.optimizers.Optimizer):
|
|
|
183
133
|
return dict(list(base_config.items()) + list(config.items()))
|
|
184
134
|
|
|
185
135
|
|
|
186
|
-
class CPNN(BaseDeepLDL):
|
|
136
|
+
class CPNN(BaseGD, BaseDeepLDL):
|
|
187
137
|
|
|
188
138
|
def _not_proper_mode(self):
|
|
189
139
|
raise ValueError("The argument 'mode' can only be 'none', 'binary' or 'augment'.")
|
|
190
140
|
|
|
191
|
-
def __init__(self, mode='none', v=5, n_hidden=
|
|
141
|
+
def __init__(self, mode='none', v=5, n_hidden=64, n_latent=None, random_state=None):
|
|
192
142
|
super().__init__(n_hidden, n_latent, random_state)
|
|
193
143
|
if mode == 'none' or mode == 'binary' or mode == 'augment':
|
|
194
144
|
self._mode = mode
|
|
@@ -196,37 +146,34 @@ class CPNN(BaseDeepLDL):
|
|
|
196
146
|
self._not_proper_mode()
|
|
197
147
|
self._v = v
|
|
198
148
|
|
|
199
|
-
|
|
200
|
-
|
|
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))
|
|
201
153
|
|
|
202
|
-
|
|
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)])
|
|
203
159
|
|
|
204
|
-
|
|
205
|
-
|
|
160
|
+
def _get_default_optimizer(self):
|
|
161
|
+
return RProp()
|
|
206
162
|
|
|
163
|
+
def _before_train(self):
|
|
207
164
|
if self._mode == 'augment':
|
|
165
|
+
n = self._X.shape[0]
|
|
208
166
|
one_hot = tf.one_hot(tf.math.argmax(self._y, axis=1), self.n_outputs)
|
|
209
167
|
self._X = tf.repeat(self._X, self._v, axis=0)
|
|
210
168
|
self._y = tf.repeat(self._y, self._v, axis=0)
|
|
211
169
|
one_hot = tf.repeat(one_hot, self._v, axis=0)
|
|
212
|
-
v = tf.reshape(tf.tile([1 / (i + 1) for i in range(self._v)], [
|
|
170
|
+
v = tf.reshape(tf.tile([1 / (i + 1) for i in range(self._v)], [n]), (-1, 1))
|
|
213
171
|
self._y += self._y * one_hot * v
|
|
214
172
|
|
|
215
|
-
input_shape = (self._n_features + (1 if self._mode == 'none' else self._n_outputs),)
|
|
216
|
-
self._model = keras.Sequential([keras.layers.InputLayer(input_shape=input_shape),
|
|
217
|
-
keras.layers.Dense(self._n_hidden, activation='sigmoid'),
|
|
218
|
-
keras.layers.Dense(1, activation=None)])
|
|
219
|
-
|
|
220
|
-
for _ in range(epochs):
|
|
221
|
-
with tf.GradientTape() as tape:
|
|
222
|
-
loss = self._loss(self._X, self._y)
|
|
223
|
-
gradients = tape.gradient(loss, self.trainable_variables)
|
|
224
|
-
self._optimizer.get_updates(self.trainable_variables, gradients)
|
|
225
|
-
|
|
226
173
|
def _make_inputs(self, X):
|
|
227
174
|
temp = tf.reshape(tf.tile([i + 1 for i in range(self._n_outputs)], [X.shape[0]]), (-1, 1))
|
|
228
175
|
if self._mode != 'none':
|
|
229
|
-
temp =
|
|
176
|
+
temp = tf.one_hot(tf.reshape(temp, (-1, )) - 1, depth=self._n_outputs)
|
|
230
177
|
return tf.concat([tf.cast(tf.repeat(X, self._n_outputs, axis=0), dtype=tf.float32),
|
|
231
178
|
tf.cast(temp, dtype=tf.float32)],
|
|
232
179
|
axis=1)
|
|
@@ -238,12 +185,6 @@ class CPNN(BaseDeepLDL):
|
|
|
238
185
|
b = tf.reshape(-tf.math.log(tf.math.reduce_sum(tf.math.exp(results), axis=1)), (-1, 1))
|
|
239
186
|
return tf.math.exp(b + results)
|
|
240
187
|
|
|
241
|
-
def _loss(self, X, y):
|
|
242
|
-
return tf.math.reduce_mean(keras.losses.kl_divergence(y, self._call(X)))
|
|
243
|
-
|
|
244
|
-
def predict(self, X):
|
|
245
|
-
return self._call(X)
|
|
246
|
-
|
|
247
188
|
|
|
248
189
|
class BCPNN(CPNN):
|
|
249
190
|
|
pyldl/algorithms/_classifier.py
CHANGED
|
@@ -1,114 +1,89 @@
|
|
|
1
1
|
import keras
|
|
2
2
|
import tensorflow as tf
|
|
3
3
|
|
|
4
|
-
from pyldl.
|
|
5
|
-
from pyldl.algorithms.base import BaseDeepLDL, BaseDeepLDLClassifier, DeepBFGS
|
|
4
|
+
from pyldl.algorithms.base import BaseDeepLDLClassifier, BaseGD, BaseBFGS
|
|
6
5
|
|
|
7
6
|
|
|
8
|
-
class LDL4C(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
super().__init__(n_hidden, n_latent, random_state)
|
|
7
|
+
class LDL4C(BaseBFGS, BaseDeepLDLClassifier):
|
|
8
|
+
"""LDL4C is proposed in paper Classification with Label Distribution Learning.
|
|
9
|
+
"""
|
|
12
10
|
|
|
13
11
|
@tf.function
|
|
14
|
-
def _loss(self,
|
|
15
|
-
y_pred = self.
|
|
12
|
+
def _loss(self, params_1d):
|
|
13
|
+
y_pred = keras.activations.softmax(self._X @ self._params2model(params_1d)[0])
|
|
16
14
|
top2 = tf.gather(y_pred, self._top2, axis=1, batch_dims=1)
|
|
17
15
|
margin = tf.reduce_sum(tf.maximum(0., 1. - (top2[:, 0] - top2[:, 1]) / self._rho))
|
|
18
|
-
mae = keras.losses.mean_absolute_error(
|
|
19
|
-
return tf.reduce_sum(self._entropy * mae) + self._alpha * margin + self._beta *
|
|
20
|
-
|
|
21
|
-
def fit(self, X, y, max_iterations=50, alpha=1e-2, beta=1e-6, rho=1e-2):
|
|
22
|
-
super().fit(X, y)
|
|
23
|
-
|
|
24
|
-
self._alpha = alpha
|
|
25
|
-
self._beta = beta
|
|
26
|
-
self._rho = 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)
|
|
27
18
|
|
|
19
|
+
def _before_train(self):
|
|
28
20
|
self._top2 = tf.math.top_k(self._y, k=2)[1]
|
|
29
21
|
self._entropy = tf.cast(-tf.reduce_sum(self._y * tf.math.log(self._y) + 1e-7, axis=1), dtype=tf.float32)
|
|
30
22
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
23
|
+
def fit(self, X, y, alpha=1e-2, beta=1e-6, rho=1e-2, **kwargs):
|
|
24
|
+
self._alpha = alpha
|
|
25
|
+
self._beta = beta
|
|
26
|
+
self._rho = rho
|
|
27
|
+
return super().fit(X, y, **kwargs)
|
|
37
28
|
|
|
38
|
-
class LDL_HR(BaseDeepLDLClassifier, DeepBFGS):
|
|
39
29
|
|
|
40
|
-
|
|
41
|
-
|
|
30
|
+
class LDL_HR(BaseBFGS, BaseDeepLDLClassifier):
|
|
31
|
+
"""LDL-HR is proposed in paper Learn the Highest Label and Rest Label Description Degrees.
|
|
32
|
+
"""
|
|
42
33
|
|
|
43
34
|
@tf.function
|
|
44
|
-
def _loss(self,
|
|
45
|
-
y_pred = self.
|
|
46
|
-
|
|
35
|
+
def _loss(self, params_1d):
|
|
36
|
+
y_pred = keras.activations.softmax(self._X @ self._params2model(params_1d)[0])
|
|
37
|
+
|
|
47
38
|
highest = tf.gather(y_pred, self._highest, axis=1, batch_dims=1)
|
|
48
39
|
rest = tf.gather(y_pred, self._rest, axis=1, batch_dims=1)
|
|
49
40
|
margin = tf.reduce_sum(tf.maximum(0., 1. - (highest - rest) / self._rho))
|
|
50
41
|
|
|
51
|
-
real_rest = tf.gather(
|
|
42
|
+
real_rest = tf.gather(self._y, self._rest, axis=1, batch_dims=1)
|
|
52
43
|
rest_mae = tf.reduce_sum(keras.losses.mean_absolute_error(real_rest, rest))
|
|
53
44
|
|
|
54
45
|
mae = tf.reduce_sum(keras.losses.mean_absolute_error(self._l, y_pred))
|
|
55
46
|
|
|
56
|
-
return mae + self._alpha * margin + self._beta * rest_mae + self._gamma *
|
|
57
|
-
|
|
58
|
-
def fit(self, X, y, max_iterations=50, alpha=1e-2, beta=1e-2, gamma=1e-6, rho=1e-2):
|
|
59
|
-
super().fit(X, y)
|
|
60
|
-
|
|
61
|
-
self._alpha = alpha
|
|
62
|
-
self._beta = beta
|
|
63
|
-
self._gamma = gamma
|
|
64
|
-
self._rho = rho
|
|
47
|
+
return mae + self._alpha * margin + self._beta * rest_mae + self._gamma * self._l2_reg(self._model)
|
|
65
48
|
|
|
49
|
+
def _before_train(self):
|
|
66
50
|
temp = tf.math.top_k(self._y, k=self._n_outputs)[1]
|
|
67
51
|
self._highest = temp[:, 0:1]
|
|
68
52
|
self._rest = temp[:, 1:]
|
|
69
|
-
|
|
70
53
|
self._l = tf.one_hot(tf.reshape(self._highest, -1), self._n_outputs)
|
|
71
54
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
self.
|
|
77
|
-
|
|
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)
|
|
78
61
|
|
|
79
|
-
class LDLM(BaseDeepLDLClassifier):
|
|
80
62
|
|
|
81
|
-
|
|
82
|
-
|
|
63
|
+
class LDLM(BaseGD, BaseDeepLDLClassifier):
|
|
64
|
+
"""LDLM is proposed in paper Label Distribution Learning Machine.
|
|
65
|
+
"""
|
|
83
66
|
|
|
84
67
|
@tf.function
|
|
85
|
-
def _loss(self, X, y):
|
|
68
|
+
def _loss(self, X, y, start, end):
|
|
86
69
|
y_pred = self._model(X)
|
|
87
70
|
|
|
88
71
|
pred_margin = tf.reduce_sum(tf.clip_by_value(
|
|
89
|
-
tf.reduce_sum(tf.abs(self._l - y_pred), axis=1) - self._rho,
|
|
72
|
+
tf.reduce_sum(tf.abs(self._l[start:end] - y_pred), axis=1) - self._rho,
|
|
90
73
|
0., float('inf')))
|
|
91
|
-
|
|
92
|
-
highest = tf.gather(y_pred, self._highest, axis=1, batch_dims=1)
|
|
93
|
-
rest = tf.gather(y_pred, self._rest, axis=1, batch_dims=1)
|
|
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)
|
|
94
77
|
label_margin = tf.reduce_sum(tf.maximum(0., 1. - (highest - rest) / self._rho))
|
|
95
78
|
|
|
96
79
|
second_margin = tf.reduce_sum(tf.clip_by_value(
|
|
97
|
-
tf.reduce_sum(tf.abs((y - y_pred) * self._neg_l), axis=1) - self._second_margin,
|
|
80
|
+
tf.reduce_sum(tf.abs((y - y_pred) * self._neg_l[start:end]), axis=1) - self._second_margin[start:end],
|
|
98
81
|
0., float('inf')))
|
|
99
82
|
|
|
100
83
|
return pred_margin + self._alpha * label_margin + \
|
|
101
|
-
self._beta * second_margin + self._gamma *
|
|
102
|
-
|
|
103
|
-
def fit(self, X, y, learning_rate=5e-4, epochs=1000,
|
|
104
|
-
alpha=1e-2, beta=1e-2, gamma=1e-6, rho=1e-2):
|
|
105
|
-
super().fit(X, y)
|
|
106
|
-
|
|
107
|
-
self._alpha = alpha
|
|
108
|
-
self._beta = beta
|
|
109
|
-
self._gamma = gamma
|
|
110
|
-
self._rho = rho
|
|
84
|
+
self._beta * second_margin + self._gamma * self._l2_reg(self._model)
|
|
111
85
|
|
|
86
|
+
def _before_train(self):
|
|
112
87
|
temp = tf.math.top_k(self._y, k=self._n_outputs)[1]
|
|
113
88
|
self._highest = temp[:, 0:1]
|
|
114
89
|
self._rest = temp[:, 1:]
|
|
@@ -119,15 +94,12 @@ class LDLM(BaseDeepLDLClassifier):
|
|
|
119
94
|
self._l = tf.one_hot(tf.reshape(self._highest, -1), self._n_outputs)
|
|
120
95
|
self._neg_l = tf.where(tf.equal(self._l, 0.), 1., 0.)
|
|
121
96
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
keras.layers.Dense(self._n_outputs, activation="softmax")])
|
|
125
|
-
|
|
126
|
-
self._optimizer = keras.optimizers.SGD(learning_rate)
|
|
127
|
-
|
|
128
|
-
for _ in range(epochs):
|
|
129
|
-
with tf.GradientTape() as tape:
|
|
130
|
-
loss = self._loss(self._X, self._y)
|
|
97
|
+
def _get_default_model(self):
|
|
98
|
+
return self.get_2layer_model(self._n_features, self._n_outputs)
|
|
131
99
|
|
|
132
|
-
|
|
133
|
-
|
|
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)
|
pyldl/algorithms/_incomplete.py
CHANGED
|
@@ -1,45 +1,58 @@
|
|
|
1
|
-
import
|
|
2
|
-
import tensorflow as tf
|
|
3
|
-
import tensorflow_addons as tfa
|
|
1
|
+
import numpy as np
|
|
4
2
|
|
|
5
|
-
from
|
|
6
|
-
from pyldl.algorithms.base import BaseDeepLDL
|
|
3
|
+
from qpsolvers import solve_qp
|
|
7
4
|
|
|
5
|
+
from pyldl.algorithms.base import BaseLDL
|
|
8
6
|
|
|
9
|
-
class IncomLDL(BaseDeepLDL):
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
class IncomLDL(BaseLDL):
|
|
9
|
+
"""IncomLDL is proposed in paper Incomplete Label Distribution Learning.
|
|
10
|
+
"""
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
fro_norm = tf.reduce_sum(tf.square(self._mask * (y_pred - y)))
|
|
20
|
-
return fro_norm / 2. + self._alpha * trace_norm
|
|
12
|
+
@staticmethod
|
|
13
|
+
def svt(A, tau):
|
|
14
|
+
U, S, VT = np.linalg.svd(A, full_matrices=False)
|
|
15
|
+
S_thresh = np.maximum(S - tau, 0)
|
|
16
|
+
return U @ np.diag(S_thresh) @ VT
|
|
21
17
|
|
|
22
|
-
def
|
|
23
|
-
|
|
18
|
+
def _update_Z(self):
|
|
19
|
+
A = self._X @ self._W + self._V / self._rho
|
|
20
|
+
tau = self._alpha / self._rho
|
|
21
|
+
self._Z = self.svt(A, tau)
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
def _update_W(self):
|
|
24
|
+
|
|
25
|
+
G = -np.eye(self._n_outputs, dtype=np.float64)
|
|
26
|
+
h = np.zeros((self._n_outputs, 1), dtype=np.float64)
|
|
27
|
+
A = np.ones((1, self._n_outputs), dtype=np.float64)
|
|
28
|
+
b = np.array([1.], dtype=np.float64)
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
keras.layers.Dense(self._n_outputs, activation='softmax', use_bias=False)])
|
|
30
|
-
self._optimizer = tfa.optimizers.ProximalAdagrad(learning_rate)
|
|
30
|
+
M = np.zeros_like(self._y)
|
|
31
31
|
|
|
32
|
-
for
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
for i in range(self._X.shape[0]):
|
|
33
|
+
P = np.diag((1 + self._rho) * self._mask[i] + self._rho * (1 - self._mask[i])).astype(np.float64)
|
|
34
|
+
ql = (self._V[i] - self._y[i] * self._mask[i] - self._rho * self._Z[i]) * self._mask[i]
|
|
35
|
+
qr = (self._V[i] - self._rho * self._Z[i]) * (1 - self._mask[i])
|
|
36
|
+
q = np.transpose(ql + qr).astype(np.float64)
|
|
37
|
+
M[i] = solve_qp(P, q, G, h, A, b, solver='quadprog')
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
self._W = np.linalg.pinv(np.transpose(self._X) @ self._X) @ np.transpose(self._X) @ M
|
|
40
|
+
|
|
41
|
+
def _update_V(self):
|
|
42
|
+
self._V = self._V + self._rho * (self._X @ self._W - self._Z)
|
|
43
|
+
|
|
44
|
+
def fit(self, X, y, mask, alpha=1e-3, rho=1., max_iterations=100):
|
|
45
|
+
super().fit(X, y)
|
|
46
|
+
self._alpha = alpha
|
|
47
|
+
self._rho = rho
|
|
48
|
+
self._mask = np.where(mask, 0., 1.)
|
|
49
|
+
self._W = np.ones((self._n_features, self._n_outputs))
|
|
50
|
+
self._Z = np.ones((self._X.shape[0], self._n_outputs))
|
|
51
|
+
self._V = np.ones((self._X.shape[0], self._n_outputs))
|
|
52
|
+
for _ in range(max_iterations):
|
|
53
|
+
self._update_W()
|
|
54
|
+
self._update_Z()
|
|
55
|
+
self._update_V()
|
|
41
56
|
|
|
42
|
-
def
|
|
43
|
-
|
|
44
|
-
metrics = ["chebyshev", "clark", "canberra", "cosine", "intersection"]
|
|
45
|
-
return score(y, self.predict(X), metrics=metrics)
|
|
57
|
+
def predict(self, X):
|
|
58
|
+
return X @ self._W
|