python-ldl 0.1.1__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.
@@ -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)