5
5
6
6
# License: MIT License
7
7
8
+ from abc import abstractmethod
9
+
8
10
import numpy as np
9
11
from scipy .special import huber
10
- from sklearn .datasets import make_classification
11
- from sklearn .preprocessing import StandardScaler
12
+ from sklearn .base import BaseEstimator
13
+ from sklearn .utils .validation import check_array , check_is_fitted , check_X_y
14
+
15
+ from ._internal import rehline_internal , rehline_result
16
+
17
+
18
+ class _BaseReHLine (BaseEstimator ):
19
+ r"""Base Class of ReHLine Formulation.
20
+
21
+ .. math::
22
+
23
+ \min_{\mathbf{\beta} \in \mathbb{R}^d} \sum_{i=1}^n \sum_{l=1}^L \text{ReLU}( u_{li} \mathbf{x}_i^\intercal \mathbf{\beta} + v_{li}) + \sum_{i=1}^n \sum_{h=1}^H {\text{ReHU}}_{\tau_{hi}}( s_{hi} \mathbf{x}_i^\intercal \mathbf{\beta} + t_{hi}) + \frac{1}{2} \| \mathbf{\beta} \|_2^2, \\ \text{ s.t. }
24
+ \mathbf{A} \mathbf{\beta} + \mathbf{b} \geq \mathbf{0},
25
+
26
+ where :math:`\mathbf{U} = (u_{li}),\mathbf{V} = (v_{li}) \in \mathbb{R}^{L \times n}`
27
+ and :math:`\mathbf{S} = (s_{hi}),\mathbf{T} = (t_{hi}),\mathbf{\tau} = (\tau_{hi}) \in \mathbb{R}^{H \times n}`
28
+ are the ReLU-ReHU loss parameters, and :math:`(\mathbf{A},\mathbf{b})` are the constraint parameters.
29
+
30
+ Parameters
31
+ ----------
12
32
33
+ C : float, default=1.0
34
+ Regularization parameter. The strength of the regularization is
35
+ inversely proportional to C. Must be strictly positive.
36
+
37
+ U, V: array of shape (L, n_samples), default=np.empty(shape=(0, 0))
38
+ The parameters pertaining to the ReLU part in the loss function.
39
+
40
+ Tau, S, T: array of shape (H, n_samples), default=np.empty(shape=(0, 0))
41
+ The parameters pertaining to the ReHU part in the loss function.
42
+
43
+ A: array of shape (K, n_features), default=np.empty(shape=(0, 0))
44
+ The coefficient matrix in the linear constraint.
45
+
46
+ b: array of shape (K, ), default=np.empty(shape=0)
47
+ The intercept vector in the linear constraint.
13
48
14
- def relu (x ):
49
+ """
50
+
51
+ def __init__ (self , C = 1. ,
52
+ U = np .empty (shape = (0 ,0 )), V = np .empty (shape = (0 ,0 )),
53
+ Tau = np .empty (shape = (0 ,0 )),
54
+ S = np .empty (shape = (0 ,0 )), T = np .empty (shape = (0 ,0 )),
55
+ A = np .empty (shape = (0 ,0 )), b = np .empty (shape = (0 ))):
56
+ self .C = C
57
+ self .U = U
58
+ self .V = V
59
+ self .S = S
60
+ self .T = T
61
+ self .Tau = Tau
62
+ self .A = A
63
+ self .b = b
64
+ self .L = U .shape [0 ]
65
+ self .n = U .shape [1 ]
66
+ self .H = S .shape [0 ]
67
+ self .K = A .shape [0 ]
68
+
69
+ def auto_shape (self ):
70
+ """
71
+ Automatically generate the shape of the parameters of the ReHLine loss function.
72
+ """
73
+ self .L = self .U .shape [0 ]
74
+ self .n = self .U .shape [1 ]
75
+ self .H = self .S .shape [0 ]
76
+ self .K = self .A .shape [0 ]
77
+
78
+ def call_ReLHLoss (self , score ):
79
+ """
80
+ Return the value of the ReHLine loss of the `score`.
81
+
82
+ Parameters
83
+ ----------
84
+ score : ndarray of shape (n_samples, )
85
+ The input score that will be evaluated through the ReHLine loss.
86
+
87
+ Returns
88
+ -------
89
+ float
90
+ ReHLine loss evaluation of the given score.
91
+ """
92
+
93
+ relu_input = np .zeros ((self .L , self .n ))
94
+ rehu_input = np .zeros ((self .H , self .n ))
95
+ if self .L > 0 :
96
+ relu_input = (self .U .T * score [:,np .newaxis ]).T + self .V
97
+ if self .H > 0 :
98
+ rehu_input = (self .S .T * score [:,np .newaxis ]).T + self .T
99
+ return np .sum (_relu (relu_input ), 0 ) + np .sum (_rehu (rehu_input ), 0 )
100
+
101
+ @abstractmethod
102
+ def fit (self , X , y , sample_weight ):
103
+ """Fit model."""
104
+
105
+ @abstractmethod
106
+ def decision_function (self , X ):
107
+ """The decision function evaluated on the given dataset
108
+
109
+ Parameters
110
+ ----------
111
+ X : array-like of shape (n_samples, n_features)
112
+ The data matrix.
113
+
114
+ Returns
115
+ -------
116
+ ndarray of shape (n_samples, )
117
+ Returns the decision function of the samples.
118
+ """
119
+ # Check if fit has been called
120
+ check_is_fitted (self )
121
+
122
+ X = check_array (X )
123
+
124
+ def _relu (x ):
15
125
"""
16
126
Evaluation of ReLU given a vector.
17
127
@@ -31,7 +141,7 @@ def relu(x):
31
141
return np .maximum (x , 0 )
32
142
33
143
34
- def rehu (x , cut = 1 ):
144
+ def _rehu (x , cut = 1 ):
35
145
"""
36
146
Evaluation of ReHU given a vector.
37
147
@@ -64,39 +174,11 @@ def _check_rehu(rehu_coef, rehu_intercept, rehu_cut):
64
174
if len (rehu_coef ) > 0 :
65
175
assert (rehu_cut >= 0.0 ).all (), "`rehu_cut` must be non-negative!"
66
176
67
- def make_fair_classification (n_samples = 100 , n_features = 5 , ind_sensitive = 0 ):
68
- """
69
- Generate a random binary fair classification problem.
70
-
71
- Parameters
72
- ----------
73
- n_samples : int, default=100
74
- The number of samples.
75
-
76
- n_features : int, default=5
77
- The total number of features.
78
-
79
- ind_sensitive : int, default=0
80
- The index of the sensitive feature.
81
-
82
- Returns
83
- -------
84
- X : ndarray of shape (n_samples, n_features)
85
- The generated samples.
86
-
87
- y : ndarray of shape (n_samples,)
88
- The +/- labels for class membership of each sample.
89
-
90
- X_sen: ndarray of shape (n_samples,)
91
- The centered samples of the sensitive feature.
92
- """
93
-
94
- X , y = make_classification (n_samples , n_features )
95
- y = 2 * y - 1
96
-
97
- scaler = StandardScaler ()
98
- X = scaler .fit_transform (X )
99
-
100
- X_sen = X [:, ind_sensitive ]
101
-
102
- return X , y , X_sen
177
+ def ReHLine_solver (X , U , V ,
178
+ Tau = np .empty (shape = (0 , 0 )),
179
+ S = np .empty (shape = (0 , 0 )), T = np .empty (shape = (0 , 0 )),
180
+ A = np .empty (shape = (0 , 0 )), b = np .empty (shape = (0 )),
181
+ max_iter = 1000 , tol = 1e-4 , shrink = 1 , verbose = 1 , trace_freq = 100 ):
182
+ result = rehline_result ()
183
+ rehline_internal (result , X , A , b , U , V , S , T , Tau , max_iter , tol , shrink , verbose , trace_freq )
184
+ return result
0 commit comments