python-ldl 0.0.1__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 +30 -0
- pyldl/algorithms/_algorithm_adaptation.py +198 -0
- pyldl/algorithms/_classifier.py +105 -0
- pyldl/algorithms/_ensemble.py +99 -0
- pyldl/algorithms/_incomplete.py +58 -0
- pyldl/algorithms/_label_enhancement.py +285 -0
- pyldl/algorithms/_ldl_da.py +182 -0
- pyldl/algorithms/_ldl_dpa.py +40 -0
- pyldl/algorithms/_ldl_lrr.py +39 -0
- pyldl/algorithms/_ldl_scl.py +54 -0
- pyldl/algorithms/_ldlf.py +84 -0
- pyldl/algorithms/_problem_transformation.py +62 -0
- pyldl/algorithms/_specialized_algorithms.py +87 -0
- pyldl/algorithms/_ssg_ldl.py +58 -0
- pyldl/algorithms/base.py +357 -0
- pyldl/applications/__init__.py +0 -0
- pyldl/applications/emphasis_selection.py +144 -0
- pyldl/applications/facial_emotion_recognition.py +209 -0
- pyldl/applications/lesion_counting.py +130 -0
- pyldl/matlab_algorithms/AA_BP_fit.m +1 -1
- pyldl/matlab_algorithms/AA_BP_predict.m +7 -7
- pyldl/matlab_algorithms/AA_KNN.m +3 -3
- pyldl/matlab_algorithms/BFGS_Process.m +13 -13
- pyldl/matlab_algorithms/PT_Bayes_fit.m +1 -1
- pyldl/matlab_algorithms/PT_Bayes_predict.m +2 -2
- pyldl/matlab_algorithms/PT_SVM_fit.m +2 -2
- pyldl/matlab_algorithms/PT_SVM_predict.m +2 -2
- pyldl/matlab_algorithms/SA_BFGS_fit.m +2 -2
- pyldl/matlab_algorithms/SA_BFGS_predict.m +1 -1
- pyldl/matlab_algorithms/SA_IIS_fit.m +6 -6
- pyldl/matlab_algorithms/SA_IIS_predict.m +1 -1
- pyldl/matlab_algorithms/__init__.py +158 -158
- pyldl/metrics.py +144 -99
- pyldl/utils.py +179 -91
- {python_ldl-0.0.1.dist-info → python_ldl-0.0.3.dist-info}/METADATA +187 -170
- python_ldl-0.0.3.dist-info/RECORD +40 -0
- {python_ldl-0.0.1.dist-info → python_ldl-0.0.3.dist-info}/WHEEL +1 -1
- pyldl/algorithms.py +0 -1373
- pyldl/rprop.py +0 -75
- python_ldl-0.0.1.dist-info/RECORD +0 -23
- {python_ldl-0.0.1.dist-info → python_ldl-0.0.3.dist-info}/LICENSE +0 -0
- {python_ldl-0.0.1.dist-info → python_ldl-0.0.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,30 @@
|
|
|
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
|
|
4
|
+
|
|
5
|
+
from ._incomplete import IncomLDL
|
|
6
|
+
from ._classifier import LDL4C, LDL_HR, LDLM
|
|
7
|
+
from ._ensemble import DF_LDL, AdaBoostLDL
|
|
8
|
+
|
|
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
|
|
13
|
+
|
|
14
|
+
from ._ssg_ldl import SSG_LDL
|
|
15
|
+
|
|
16
|
+
from ._label_enhancement import FCM, KM, LP, ML, GLLE, LEVI, LIBLE
|
|
17
|
+
|
|
18
|
+
from ._ldl_da import LDL_DA
|
|
19
|
+
|
|
20
|
+
|
|
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"]
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from sklearn.neighbors import NearestNeighbors
|
|
3
|
+
|
|
4
|
+
import keras
|
|
5
|
+
import tensorflow as tf
|
|
6
|
+
from keras import backend as K
|
|
7
|
+
|
|
8
|
+
from pyldl.algorithms.base import BaseLDL, BaseDeepLDL, BaseGD, BaseAdam
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AA_KNN(BaseLDL):
|
|
12
|
+
|
|
13
|
+
def fit(self, X, y, k=5):
|
|
14
|
+
super().fit(X, y)
|
|
15
|
+
self._model = NearestNeighbors(n_neighbors=k).fit(self._X)
|
|
16
|
+
return self
|
|
17
|
+
|
|
18
|
+
def predict(self, X):
|
|
19
|
+
_, inds = self._model.kneighbors(X)
|
|
20
|
+
return np.average(self._y[inds], axis=1)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AA_BP(BaseGD, BaseDeepLDL):
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
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)
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import keras
|
|
2
|
+
import tensorflow as tf
|
|
3
|
+
|
|
4
|
+
from pyldl.algorithms.base import BaseDeepLDLClassifier, BaseGD, BaseBFGS
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class LDL4C(BaseBFGS, BaseDeepLDLClassifier):
|
|
8
|
+
"""LDL4C is proposed in paper Classification with Label Distribution Learning.
|
|
9
|
+
"""
|
|
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
|
+
|
|
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)
|
|
22
|
+
|
|
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)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class LDL_HR(BaseBFGS, BaseDeepLDLClassifier):
|
|
31
|
+
"""LDL-HR is proposed in paper Learn the Highest Label and Rest Label Description Degrees.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
@tf.function
|
|
35
|
+
def _loss(self, params_1d):
|
|
36
|
+
y_pred = keras.activations.softmax(self._X @ self._params2model(params_1d)[0])
|
|
37
|
+
|
|
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))
|
|
41
|
+
|
|
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))
|
|
44
|
+
|
|
45
|
+
mae = tf.reduce_sum(keras.losses.mean_absolute_error(self._l, y_pred))
|
|
46
|
+
|
|
47
|
+
return mae + self._alpha * margin + self._beta * rest_mae + self._gamma * self._l2_reg(self._model)
|
|
48
|
+
|
|
49
|
+
def _before_train(self):
|
|
50
|
+
temp = tf.math.top_k(self._y, k=self._n_outputs)[1]
|
|
51
|
+
self._highest = temp[:, 0:1]
|
|
52
|
+
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)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class LDLM(BaseGD, BaseDeepLDLClassifier):
|
|
64
|
+
"""LDLM is proposed in paper Label Distribution Learning Machine.
|
|
65
|
+
"""
|
|
66
|
+
|
|
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))
|
|
78
|
+
|
|
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')))
|
|
82
|
+
|
|
83
|
+
return pred_margin + self._alpha * label_margin + \
|
|
84
|
+
self._beta * second_margin + self._gamma * self._l2_reg(self._model)
|
|
85
|
+
|
|
86
|
+
def _before_train(self):
|
|
87
|
+
temp = tf.math.top_k(self._y, k=self._n_outputs)[1]
|
|
88
|
+
self._highest = temp[:, 0:1]
|
|
89
|
+
self._rest = temp[:, 1:]
|
|
90
|
+
|
|
91
|
+
temp = tf.math.top_k(tf.gather(self._y, self._rest, axis=1, batch_dims=1), k=2)[0]
|
|
92
|
+
self._second_margin = temp[:, 0] - temp[:, 1]
|
|
93
|
+
|
|
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.)
|
|
96
|
+
|
|
97
|
+
def _get_default_model(self):
|
|
98
|
+
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)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from pyldl.algorithms.base import BaseEnsemble
|
|
6
|
+
from pyldl.metrics import sort_loss
|
|
7
|
+
from ._algorithm_adaptation import AA_KNN
|
|
8
|
+
from ._specialized_algorithms import SA_BFGS
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DF_LDL(BaseEnsemble):
|
|
12
|
+
|
|
13
|
+
def __init__(self, estimator=SA_BFGS(), random_state=None):
|
|
14
|
+
super().__init__(estimator, None, random_state)
|
|
15
|
+
|
|
16
|
+
def fit(self, X, y):
|
|
17
|
+
super().fit(X, y)
|
|
18
|
+
|
|
19
|
+
m, c = self._y.shape[0], self._y.shape[1]
|
|
20
|
+
L = {}
|
|
21
|
+
|
|
22
|
+
for i in range(c):
|
|
23
|
+
for j in range(i + 1, c):
|
|
24
|
+
|
|
25
|
+
ss1 = []
|
|
26
|
+
ss2 = []
|
|
27
|
+
|
|
28
|
+
for k in range(m):
|
|
29
|
+
if self._y[k, i] >= self._y[k, j]:
|
|
30
|
+
ss1.append(k)
|
|
31
|
+
else:
|
|
32
|
+
ss2.append(k)
|
|
33
|
+
|
|
34
|
+
l1 = copy.deepcopy(self._estimator)
|
|
35
|
+
l1.fit(self._X[ss1], self._y[ss1])
|
|
36
|
+
L[str(i)+","+str(j)] = copy.deepcopy(l1)
|
|
37
|
+
|
|
38
|
+
l2 = copy.deepcopy(self._estimator)
|
|
39
|
+
l2.fit(self._X[ss2], self._y[ss2])
|
|
40
|
+
L[str(j)+","+str(i)] = copy.deepcopy(l2)
|
|
41
|
+
|
|
42
|
+
self._estimators = L
|
|
43
|
+
|
|
44
|
+
self._knn = AA_KNN()
|
|
45
|
+
self._knn.fit(self._X, self._y)
|
|
46
|
+
|
|
47
|
+
def predict(self, X):
|
|
48
|
+
|
|
49
|
+
m, c = X.shape[0], self._y.shape[1]
|
|
50
|
+
p_knn = self._knn.predict(X)
|
|
51
|
+
p = np.zeros((m, c), dtype=np.float32)
|
|
52
|
+
|
|
53
|
+
for k in range(m):
|
|
54
|
+
for i in range(c):
|
|
55
|
+
for j in range(i + 1, c):
|
|
56
|
+
|
|
57
|
+
if p_knn[k, i] >= p_knn[k, j]:
|
|
58
|
+
l = self._estimators[str(i)+","+str(j)]
|
|
59
|
+
else:
|
|
60
|
+
l = self._estimators[str(j)+","+str(i)]
|
|
61
|
+
|
|
62
|
+
p[k] += l.predict(X[k].reshape(1, -1)).reshape(-1)
|
|
63
|
+
|
|
64
|
+
return p / (c * (c - 1) / 2)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class AdaBoostLDL(BaseEnsemble):
|
|
68
|
+
|
|
69
|
+
def __init__(self, estimator=SA_BFGS(), n_estimators=10, random_state=None):
|
|
70
|
+
super().__init__(estimator, n_estimators, random_state)
|
|
71
|
+
|
|
72
|
+
def fit(self, X, y, loss=sort_loss, alpha=1.):
|
|
73
|
+
super().fit(X, y)
|
|
74
|
+
|
|
75
|
+
m = self._X.shape[0]
|
|
76
|
+
p = np.ones((m,)) / m
|
|
77
|
+
|
|
78
|
+
self._loss = np.zeros((self._n_estimators, m))
|
|
79
|
+
self._estimators = []
|
|
80
|
+
for i in range(self._n_estimators):
|
|
81
|
+
select = np.random.choice(m, size=m, p=p)
|
|
82
|
+
X_train, y_train = self._X[select], self._y[select]
|
|
83
|
+
|
|
84
|
+
model = copy.deepcopy(self._estimator)
|
|
85
|
+
model.fit(X_train, y_train)
|
|
86
|
+
self._estimators.append(copy.deepcopy(model))
|
|
87
|
+
|
|
88
|
+
y_pred = model.predict(self._X)
|
|
89
|
+
self._loss[i] = loss(y, y_pred, reduction=None)
|
|
90
|
+
p += alpha * (self._loss[i] / np.sum(self._loss))
|
|
91
|
+
p /= np.sum(p)
|
|
92
|
+
|
|
93
|
+
def predict(self, X):
|
|
94
|
+
w = np.sum(self._loss, axis=1)
|
|
95
|
+
w /= np.sum(w)
|
|
96
|
+
y = np.zeros((X.shape[0], self._n_outputs))
|
|
97
|
+
for i in range(self._n_estimators):
|
|
98
|
+
y += w[i] * self._estimators[i].predict(X)
|
|
99
|
+
return y
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from qpsolvers import solve_qp
|
|
4
|
+
|
|
5
|
+
from pyldl.algorithms.base import BaseLDL
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class IncomLDL(BaseLDL):
|
|
9
|
+
"""IncomLDL is proposed in paper Incomplete Label Distribution Learning.
|
|
10
|
+
"""
|
|
11
|
+
|
|
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
|
|
17
|
+
|
|
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)
|
|
22
|
+
|
|
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)
|
|
29
|
+
|
|
30
|
+
M = np.zeros_like(self._y)
|
|
31
|
+
|
|
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')
|
|
38
|
+
|
|
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()
|
|
56
|
+
|
|
57
|
+
def predict(self, X):
|
|
58
|
+
return X @ self._W
|