python-ldl 0.1.0__py3-none-any.whl → 0.1.2__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.
@@ -25,9 +25,9 @@ class Duo_LDL(BaseAdam, BaseDeepLDL):
25
25
  , axis=1)
26
26
 
27
27
  def predict(self, X):
28
- from pyldl.algorithms.utils import normalize
28
+ from pyldl.algorithms.utils import proj
29
29
  C_pred = self._call(X)
30
30
  shape = (X.shape[0], self._n_outputs - 1, self._n_outputs)
31
31
  C_pred_reshaped = ops.transpose(ops.reshape(C_pred, shape), (0, 2, 1))
32
32
  D_pred = self._to_numpy((ops.sum(C_pred_reshaped, axis=2) + 1) / self._n_outputs)
33
- return normalize(D_pred)
33
+ return proj(D_pred)
@@ -36,4 +36,4 @@ class LDL_DPA(BaseBFGS, BaseDeepLDL):
36
36
 
37
37
  def _before_train(self):
38
38
  from scipy.stats import rankdata
39
- self._R = ops.cast(rankdata(self._D, axis=1), dtype="float32")
39
+ self._R = ops.cast(rankdata(self._to_numpy(self._D), axis=1), dtype="float32")
@@ -4,17 +4,25 @@ from numba import jit
4
4
 
5
5
  from scipy.optimize import minimize
6
6
  from scipy.special import softmax
7
+ from scipy.spatial.distance import cdist
7
8
 
8
9
  from sklearn.cluster import KMeans
9
10
 
10
11
  from pyldl.algorithms.base import BaseADMM, BaseLDL
11
- from pyldl.algorithms.utils import svt, solvel21, pairwise_euclidean
12
+ from pyldl.algorithms.utils import svt, solvel21
13
+
14
+
15
+ @jit(nopython=True)
16
+ def _get_log_D_pred(X, W):
17
+ XW = X @ W
18
+ for i in range(XW.shape[0]):
19
+ XW[i] -= np.max(XW[i])
20
+ return XW - np.log(np.sum(np.exp(XW), axis=1)).reshape(-1, 1)
12
21
 
13
22
 
14
23
  @jit(nopython=True)
15
24
  def _get_D_pred(X, W):
16
- exp_XW = np.exp(X @ W)
17
- return exp_XW / np.sum(exp_XW, axis=1).reshape(-1, 1)
25
+ return np.exp(_get_log_D_pred(X, W))
18
26
 
19
27
 
20
28
  @jit(nopython=True)
@@ -25,17 +33,19 @@ def _get_D_pred_DSE(D, S, E, X, W):
25
33
 
26
34
  @jit(nopython=True)
27
35
  def _update_W_numba(X, D, W, S, E, V, alpha, rho):
36
+ log_D_pred = _get_log_D_pred(X, W)
28
37
  D_pred, DSE = _get_D_pred_DSE(D, S, E, X, W)
29
- DD2 = D_pred - D_pred ** 2
30
- kl = np.sum(D * (np.log(D) - np.log(D_pred)))
38
+ G = -(V + rho * DSE) @ S.T
39
+ D_flat = D.reshape(-1, )
40
+ mask = D_flat > 0
41
+ kl = np.sum(D_flat[mask] * (np.log(D_flat[mask]) - log_D_pred.reshape(-1, )[mask]))
31
42
  inn = np.sum(V * DSE)
32
43
  fro1 = np.linalg.norm(W) ** 2
33
44
  fro2 = rho * np.linalg.norm(DSE) ** 2 / 2.
34
45
  loss = kl + inn + alpha * fro1 + fro2
35
46
  grad = X.T @ (D_pred - D)
36
47
  grad += 2 * alpha * W
37
- grad -= X.T @ (DD2 * V) @ S.T
38
- grad -= rho * X.T @ (DD2 * DSE) @ S.T
48
+ grad += X.T @ (D_pred * (G - np.sum(D_pred * G, axis=1).reshape(-1, 1)))
39
49
  return loss, grad.reshape(-1, )
40
50
 
41
51
 
@@ -52,7 +62,7 @@ def _update_S_numba(X, D, W, S, E, Z, V, V2, P, sumP, n_clusters, delta, rho):
52
62
  for i in range(n_clusters):
53
63
  pairwise -= np.sum(S * P[i])
54
64
  loss = inn + fro + delta * pairwise
55
- grad = - V.T @ D_pred + V2.T
65
+ grad = - D_pred.T @ V + V2
56
66
  grad += rho * (S - Z - D_pred.T @ DSE)
57
67
  grad -= sumP
58
68
  return loss, grad.reshape(-1, )
@@ -80,19 +90,21 @@ class LDL_LCLR(BaseADMM, BaseLDL):
80
90
  self.delta = delta
81
91
 
82
92
  def _update_W(self):
83
- """Please note that Eq. (9) in paper :cite:`2019:ren2` should be corrected to:
93
+ r"""The gradient of Eq. (7) in paper :cite:`2019:ren2`, correcting Eq. (9), is:
84
94
 
85
95
  .. math::
86
96
 
87
- \\begin{aligned}
88
- \\nabla_\\boldsymbol{W} = & \\boldsymbol{X}^{\\top} \\left(\\hat{\\boldsymbol{D}} - \\boldsymbol{D}\\right) + 2 \\lambda_1 \\boldsymbol{W} -
89
- \\boldsymbol{X}^{\\top} \\left(\\left(\\hat{\\boldsymbol{D}} - \\hat{\\boldsymbol{D}}^2\\right) \\odot
90
- \\boldsymbol{\\Gamma}_1\\right) \\boldsymbol{S}^{\\top} \\\\
91
- - & \\rho \\boldsymbol{X}^{\\top} \\left(\\left(\\hat{\\boldsymbol{D}} - \\hat{\\boldsymbol{D}}^2\\right) \\odot
92
- \\left(\\boldsymbol{D} - \\hat{\\boldsymbol{D}}\\boldsymbol{S} - \\boldsymbol{E}\\right)\\right) \\boldsymbol{S}^{\\top}\\text{,}
93
- \\end{aligned}
94
-
95
- where :math:`\\odot` denotes element-wise multiplication.
97
+ \begin{aligned}
98
+ \hat{\boldsymbol{D}} &= \operatorname{softmax}(\boldsymbol{X}\boldsymbol{W}), \\
99
+ \boldsymbol{R} &= \boldsymbol{D} - \hat{\boldsymbol{D}}\boldsymbol{S} - \boldsymbol{E}, \\
100
+ \boldsymbol{G} &= -(\boldsymbol{\Gamma}_1 + \rho\boldsymbol{R})\boldsymbol{S}^{\top}, \\
101
+ \nabla_{\boldsymbol{W}} &= \boldsymbol{X}^{\top}[
102
+ \hat{\boldsymbol{D}} - \boldsymbol{D} + \hat{\boldsymbol{D}}\odot(
103
+ \boldsymbol{G} - ((\hat{\boldsymbol{D}}\odot\boldsymbol{G})\boldsymbol{1})
104
+ \boldsymbol{1}^{\top})] + 2\lambda_1\boldsymbol{W}.
105
+ \end{aligned}
106
+
107
+ Here :math:`\odot` denotes element-wise multiplication.
96
108
  """
97
109
 
98
110
  def _obj_func(w):
@@ -106,6 +118,21 @@ class LDL_LCLR(BaseADMM, BaseLDL):
106
118
  self._update_E()
107
119
 
108
120
  def _update_S(self):
121
+ r"""The gradient of Eq. (8) in paper :cite:`2019:ren2`, correcting Eq. (10), is:
122
+
123
+ .. math::
124
+
125
+ \begin{aligned}
126
+ \boldsymbol{R} &= \boldsymbol{D} - \hat{\boldsymbol{D}}\boldsymbol{S} - \boldsymbol{E}, \\
127
+ \nabla_{\boldsymbol{S}} &= -\hat{\boldsymbol{D}}^{\top}\boldsymbol{\Gamma}_1
128
+ + \boldsymbol{\Gamma}_2
129
+ + \rho(\boldsymbol{S} - \boldsymbol{Z}
130
+ - \hat{\boldsymbol{D}}^{\top}\boldsymbol{R})
131
+ - \frac{\lambda_4}{2}\sum_v\boldsymbol{P}_v.
132
+ \end{aligned}
133
+
134
+ Here :math:`(\boldsymbol{P}_v)_{mn} = \|\boldsymbol{D}^{v}_{\cdot m} - \boldsymbol{D}^{v}_{\cdot n}\|_2^2`.
135
+ """
109
136
 
110
137
  def _obj_func(s):
111
138
  self._S = s.reshape(self._n_outputs, self._n_outputs)
@@ -117,11 +144,11 @@ class LDL_LCLR(BaseADMM, BaseLDL):
117
144
  self._S = optimize_result.x.reshape(self._n_outputs, self._n_outputs)
118
145
 
119
146
  def _update_E(self):
120
- _, DSE = _get_D_pred_DSE(self._D, self._S, self._E, self._X, self._W)
121
- self._E = solvel21(DSE, self.beta / self._rho)
147
+ D_pred = _get_D_pred(self._X, self._W)
148
+ self._E = solvel21(self._D - D_pred @ self._S + self._V / self._rho, self.beta / self._rho)
122
149
 
123
150
  def _update_Z(self):
124
- self._Z = svt(self._S - self._Z, self.gamma / self._rho)
151
+ self._Z = svt(self._S + self._V2 / self._rho, self.gamma / self._rho)
125
152
 
126
153
  def _update_V(self):
127
154
  self._V, self._V2 = _update_V_numba(self._X, self._D, self._W, self._S, self._E,
@@ -150,10 +177,11 @@ class LDL_LCLR(BaseADMM, BaseLDL):
150
177
  def _before_train(self):
151
178
  c = KMeans(n_clusters=self.n_clusters).fit_predict(self._D)
152
179
  self._P = []
153
- self._sumP = 0.
180
+ self._sumP = np.zeros((self._n_outputs, self._n_outputs))
154
181
  for i in range(self.n_clusters):
155
- temp = pairwise_euclidean(self._D[c == i].T)
156
- self._sumP += self.delta * np.sum(temp)
182
+ D_cluster = self._D[c == i].T
183
+ temp = cdist(D_cluster, D_cluster, metric='sqeuclidean')
184
+ self._sumP += self.delta * temp
157
185
  self._P.append(temp)
158
186
  self._S = np.eye(self._n_outputs)
159
187
  self._E = np.zeros((self._n_samples, self._n_outputs))
@@ -18,17 +18,12 @@ class LDL_LRR(BaseBFGS, BaseDeepLDL):
18
18
  self.beta = beta
19
19
 
20
20
  @staticmethod
21
- def ranking_loss(D_pred, P, W):
21
+ def ranking_loss(D_pred, P, W, sigma=1e2):
22
22
  logsig = lambda x: -ops.logaddexp(0., -x)
23
- P_hat = D_pred[:, :, None] - D_pred[:, None, :]
24
- l = ((1 - P) * logsig(1 - P_hat) + P * logsig(P_hat)) * W
23
+ P_hat = sigma * (D_pred[:, :, None] - D_pred[:, None, :])
24
+ l = ((1 - P) * logsig(-P_hat) + P * logsig(P_hat)) * W
25
25
  return -ops.sum(l)
26
26
 
27
- @staticmethod
28
- def preprocessing(D):
29
- diff = D[:, :, None] - D[:, None, :]
30
- return ops.where(diff > .5, 1., 0.), ops.square(diff)
31
-
32
27
  def _loss(self, params_1d):
33
28
  theta = self._params2model(params_1d)[0]
34
29
  D_pred = keras.activations.softmax(self._X @ theta)
@@ -37,4 +32,6 @@ class LDL_LRR(BaseBFGS, BaseDeepLDL):
37
32
  return kld + self.alpha * rnk + self.beta * self._l2_reg(theta)
38
33
 
39
34
  def _before_train(self):
40
- self._P, self._W = LDL_LRR.preprocessing(self._D)
35
+ diff = self._D[:, :, None] - self._D[:, None, :]
36
+ self._P = ops.where(diff > 0., 1., ops.where(diff < 0., 0., .5))
37
+ self._W = ops.square(diff)
@@ -35,7 +35,7 @@ class LDL_SCL(BaseAdam, BaseDeepLDL):
35
35
  self.beta = beta
36
36
 
37
37
  def _get_default_model(self):
38
- return self.get_2layer_model(self._n_features, self._n_outputs)
38
+ return self.get_2layer_model(self._n_features, self._n_outputs, activation='linear')
39
39
 
40
40
  def _before_train(self):
41
41
  self._P = ops.convert_to_tensor(
@@ -51,6 +51,10 @@ class LDL_SCL(BaseAdam, BaseDeepLDL):
51
51
  initializer=keras.initializers.RandomNormal(), trainable=True
52
52
  )
53
53
 
54
+ def train_step(self, *args, **kwargs):
55
+ super().train_step(*args, **kwargs)
56
+ self._C.assign(ops.maximum(self._C, EPS))
57
+
54
58
  @staticmethod
55
59
  def scl_loss(D_pred, P, C):
56
60
  corr = ops.mean(C * keras.losses.mean_squared_error(
@@ -22,5 +22,7 @@ class LDLLC(BaseBFGS, BaseDeepLDL):
22
22
  thetaT = ops.transpose(theta)
23
23
  D_pred = keras.activations.softmax(self._X @ theta)
24
24
  kld = ops.sum(keras.losses.kl_divergence(self._D, D_pred))
25
- lc = ops.sum(non_diagonal(ops.sign(ops.corrcoef(thetaT)) * pairwise_euclidean(thetaT))) / 2.
25
+ centered = thetaT - ops.mean(thetaT, axis=1, keepdims=True)
26
+ signs = ops.sign(centered @ ops.transpose(centered))
27
+ lc = ops.sum(non_diagonal(signs * pairwise_euclidean(thetaT))) / 2.
26
28
  return kld + self.alpha * lc + self.beta * self._l2_reg(theta)
@@ -11,8 +11,8 @@ class _LRLDL(BaseADMM, BaseLDL):
11
11
  :term:`ADMM` is used as optimization algorithm.
12
12
  """
13
13
 
14
- def __init__(self, mode='threshold', param=None, alpha=1e-3, beta=1e-3, random_state=None, **kwargs):
15
- super().__init__(random_state, **kwargs)
14
+ def __init__(self, mode='threshold', param=None, alpha=1e-3, beta=1e-3, **kwargs):
15
+ super().__init__(**kwargs)
16
16
  self._mode = mode
17
17
  self._param = param
18
18
  self._alpha = alpha