|
3 | 3 | from numba import int32, float64 |
4 | 4 |
|
5 | 5 | from skglm.datafits.base import BaseDatafit |
| 6 | +from skglm.datafits.single_task import Logistic |
6 | 7 |
|
7 | 8 |
|
8 | 9 | class QuadraticGroup(BaseDatafit): |
@@ -71,3 +72,63 @@ def gradient_scalar(self, X, y, w, Xw, j): |
71 | 72 |
|
72 | 73 | def intercept_update_step(self, y, Xw): |
73 | 74 | return np.mean(Xw - y) |
| 75 | + |
| 76 | + |
| 77 | +class LogisticGroup(Logistic): |
| 78 | + r"""Logistic datafit used with group penalties. |
| 79 | +
|
| 80 | + The datafit reads:: |
| 81 | +
|
| 82 | + (1 / n_samples) * \sum_i log(1 + exp(-y_i * Xw_i)) |
| 83 | +
|
| 84 | + Attributes |
| 85 | + ---------- |
| 86 | + grp_indices : array, shape (n_features,) |
| 87 | + The group indices stacked contiguously |
| 88 | + ([grp1_indices, grp2_indices, ...]). |
| 89 | +
|
| 90 | + grp_ptr : array, shape (n_groups + 1,) |
| 91 | + The group pointers such that two consecutive elements delimit |
| 92 | + the indices of a group in ``grp_indices``. |
| 93 | +
|
| 94 | + lipschitz : array, shape (n_groups,) |
| 95 | + The lipschitz constants for each group. |
| 96 | + """ |
| 97 | + |
| 98 | + def __init__(self, grp_ptr, grp_indices): |
| 99 | + self.grp_ptr, self.grp_indices = grp_ptr, grp_indices |
| 100 | + |
| 101 | + def get_spec(self): |
| 102 | + spec = ( |
| 103 | + ('grp_ptr', int32[:]), |
| 104 | + ('grp_indices', int32[:]), |
| 105 | + ('lipschitz', float64[:]) |
| 106 | + ) |
| 107 | + return spec |
| 108 | + |
| 109 | + def params_to_dict(self): |
| 110 | + return dict(grp_ptr=self.grp_ptr, |
| 111 | + grp_indices=self.grp_indices) |
| 112 | + |
| 113 | + def initialize(self, X, y): |
| 114 | + grp_ptr, grp_indices = self.grp_ptr, self.grp_indices |
| 115 | + n_groups = len(grp_ptr) - 1 |
| 116 | + |
| 117 | + lipschitz = np.zeros(n_groups) |
| 118 | + for g in range(n_groups): |
| 119 | + grp_g_indices = grp_indices[grp_ptr[g]: grp_ptr[g+1]] |
| 120 | + X_g = X[:, grp_g_indices] |
| 121 | + lipschitz[g] = norm(X_g, ord=2) ** 2 / (4 * len(y)) |
| 122 | + |
| 123 | + self.lipschitz = lipschitz |
| 124 | + |
| 125 | + def gradient_g(self, X, y, w, Xw, g): |
| 126 | + grp_ptr, grp_indices = self.grp_ptr, self.grp_indices |
| 127 | + grp_g_indices = grp_indices[grp_ptr[g]: grp_ptr[g+1]] |
| 128 | + raw_grad_val = self.raw_grad(y, Xw) |
| 129 | + |
| 130 | + grad_g = np.zeros(len(grp_g_indices)) |
| 131 | + for idx, j in enumerate(grp_g_indices): |
| 132 | + grad_g[idx] = X[:, j] @ raw_grad_val |
| 133 | + |
| 134 | + return grad_g |
0 commit comments