1
- """Author: Arthur Mensch
1
+ """Author: Arthur Mensch, Nelle Varoquaux
2
2
3
3
Benchmarks of sklearn SAGA vs lightning SAGA vs Liblinear. Shows the gain
4
4
in using multinomial logistic regression in term of learning time.
5
5
"""
6
6
import json
7
7
import time
8
- from os . path import expanduser
8
+ import os
9
9
10
10
from joblib import delayed , Parallel , Memory
11
11
import matplotlib .pyplot as plt
21
21
22
22
23
23
def fit_single (solver , X , y , penalty = 'l2' , single_target = True , C = 1 ,
24
- max_iter = 10 , skip_slow = False ):
24
+ max_iter = 10 , skip_slow = False , dtype = np . float64 ):
25
25
if skip_slow and solver == 'lightning' and penalty == 'l1' :
26
26
print ('skip_slowping l1 logistic regression with solver lightning.' )
27
27
return
@@ -37,7 +37,8 @@ def fit_single(solver, X, y, penalty='l2', single_target=True, C=1,
37
37
multi_class = 'ovr'
38
38
else :
39
39
multi_class = 'multinomial'
40
-
40
+ X = X .astype (dtype )
41
+ y = y .astype (dtype )
41
42
X_train , X_test , y_train , y_test = train_test_split (X , y , random_state = 42 ,
42
43
stratify = y )
43
44
n_samples = X_train .shape [0 ]
@@ -69,11 +70,15 @@ def fit_single(solver, X, y, penalty='l2', single_target=True, C=1,
69
70
multi_class = multi_class ,
70
71
C = C ,
71
72
penalty = penalty ,
72
- fit_intercept = False , tol = 1e-24 ,
73
+ fit_intercept = False , tol = 0 ,
73
74
max_iter = this_max_iter ,
74
75
random_state = 42 ,
75
76
)
77
+
78
+ # Makes cpu cache even for all fit calls
79
+ X_train .max ()
76
80
t0 = time .clock ()
81
+
77
82
lr .fit (X_train , y_train )
78
83
train_time = time .clock () - t0
79
84
@@ -106,9 +111,13 @@ def _predict_proba(lr, X):
106
111
return softmax (pred )
107
112
108
113
109
- def exp (solvers , penalties , single_target , n_samples = 30000 , max_iter = 20 ,
114
+ def exp (solvers , penalty , single_target ,
115
+ n_samples = 30000 , max_iter = 20 ,
110
116
dataset = 'rcv1' , n_jobs = 1 , skip_slow = False ):
111
- mem = Memory (cachedir = expanduser ('~/cache' ), verbose = 0 )
117
+ dtypes_mapping = {
118
+ "float64" : np .float64 ,
119
+ "float32" : np .float32 ,
120
+ }
112
121
113
122
if dataset == 'rcv1' :
114
123
rcv1 = fetch_rcv1 ()
@@ -151,21 +160,24 @@ def exp(solvers, penalties, single_target, n_samples=30000, max_iter=20,
151
160
X = X [:n_samples ]
152
161
y = y [:n_samples ]
153
162
154
- cached_fit = mem .cache (fit_single )
155
163
out = Parallel (n_jobs = n_jobs , mmap_mode = None )(
156
- delayed (cached_fit )(solver , X , y ,
164
+ delayed (fit_single )(solver , X , y ,
157
165
penalty = penalty , single_target = single_target ,
166
+ dtype = dtype ,
158
167
C = 1 , max_iter = max_iter , skip_slow = skip_slow )
159
168
for solver in solvers
160
- for penalty in penalties )
169
+ for dtype in dtypes_mapping . values () )
161
170
162
171
res = []
163
172
idx = 0
164
- for solver in solvers :
165
- for penalty in penalties :
166
- if not (skip_slow and solver == 'lightning' and penalty == 'l1' ):
173
+ for dtype_name in dtypes_mapping .keys ():
174
+ for solver in solvers :
175
+ if not (skip_slow and
176
+ solver == 'lightning' and
177
+ penalty == 'l1' ):
167
178
lr , times , train_scores , test_scores , accuracies = out [idx ]
168
179
this_res = dict (solver = solver , penalty = penalty ,
180
+ dtype = dtype_name ,
169
181
single_target = single_target ,
170
182
times = times , train_scores = train_scores ,
171
183
test_scores = test_scores ,
@@ -177,68 +189,117 @@ def exp(solvers, penalties, single_target, n_samples=30000, max_iter=20,
177
189
json .dump (res , f )
178
190
179
191
180
- def plot ():
192
+ def plot (outname = None ):
181
193
import pandas as pd
182
194
with open ('bench_saga.json' , 'r' ) as f :
183
195
f = json .load (f )
184
196
res = pd .DataFrame (f )
185
- res .set_index (['single_target' , 'penalty' ], inplace = True )
197
+ res .set_index (['single_target' ], inplace = True )
186
198
187
- grouped = res .groupby (level = ['single_target' , 'penalty' ])
199
+ grouped = res .groupby (level = ['single_target' ])
188
200
189
- colors = {'saga' : 'blue' , 'liblinear' : 'orange' , 'lightning' : 'green' }
201
+ colors = {'saga' : 'C0' , 'liblinear' : 'C1' , 'lightning' : 'C2' }
202
+ linestyles = {"float32" : "--" , "float64" : "-" }
203
+ alpha = {"float64" : 0.5 , "float32" : 1 }
190
204
191
205
for idx , group in grouped :
192
- single_target , penalty = idx
193
- fig = plt .figure (figsize = (12 , 4 ))
194
- ax = fig .add_subplot (131 )
195
-
196
- train_scores = group ['train_scores' ].values
197
- ref = np .min (np .concatenate (train_scores )) * 0.999
198
-
199
- for scores , times , solver in zip (group ['train_scores' ], group ['times' ],
200
- group ['solver' ]):
201
- scores = scores / ref - 1
202
- ax .plot (times , scores , label = solver , color = colors [solver ])
206
+ single_target = idx
207
+ fig , axes = plt .subplots (figsize = (12 , 4 ), ncols = 4 )
208
+ ax = axes [0 ]
209
+
210
+ for scores , times , solver , dtype in zip (group ['train_scores' ],
211
+ group ['times' ],
212
+ group ['solver' ],
213
+ group ["dtype" ]):
214
+ ax .plot (times , scores , label = "%s - %s" % (solver , dtype ),
215
+ color = colors [solver ],
216
+ alpha = alpha [dtype ],
217
+ marker = "." ,
218
+ linestyle = linestyles [dtype ])
219
+ ax .axvline (times [- 1 ], color = colors [solver ],
220
+ alpha = alpha [dtype ],
221
+ linestyle = linestyles [dtype ])
203
222
ax .set_xlabel ('Time (s)' )
204
223
ax .set_ylabel ('Training objective (relative to min)' )
205
224
ax .set_yscale ('log' )
206
225
207
- ax = fig . add_subplot ( 132 )
226
+ ax = axes [ 1 ]
208
227
209
- test_scores = group ['test_scores' ].values
210
- ref = np .min (np .concatenate (test_scores )) * 0.999
228
+ for scores , times , solver , dtype in zip (group ['test_scores' ],
229
+ group ['times' ],
230
+ group ['solver' ],
231
+ group ["dtype" ]):
232
+ ax .plot (times , scores , label = solver , color = colors [solver ],
233
+ linestyle = linestyles [dtype ],
234
+ marker = "." ,
235
+ alpha = alpha [dtype ])
236
+ ax .axvline (times [- 1 ], color = colors [solver ],
237
+ alpha = alpha [dtype ],
238
+ linestyle = linestyles [dtype ])
211
239
212
- for scores , times , solver in zip (group ['test_scores' ], group ['times' ],
213
- group ['solver' ]):
214
- scores = scores / ref - 1
215
- ax .plot (times , scores , label = solver , color = colors [solver ])
216
240
ax .set_xlabel ('Time (s)' )
217
241
ax .set_ylabel ('Test objective (relative to min)' )
218
242
ax .set_yscale ('log' )
219
243
220
- ax = fig .add_subplot (133 )
244
+ ax = axes [2 ]
245
+ for accuracy , times , solver , dtype in zip (group ['accuracies' ],
246
+ group ['times' ],
247
+ group ['solver' ],
248
+ group ["dtype" ]):
249
+ ax .plot (times , accuracy , label = "%s - %s" % (solver , dtype ),
250
+ alpha = alpha [dtype ],
251
+ marker = "." ,
252
+ color = colors [solver ], linestyle = linestyles [dtype ])
253
+ ax .axvline (times [- 1 ], color= colors [solver ],
254
+ alpha = alpha [dtype ],
255
+ linestyle = linestyles [dtype ])
221
256
222
- for accuracy , times , solver in zip (group ['accuracies' ], group ['times' ],
223
- group ['solver' ]):
224
- ax .plot (times , accuracy , label = solver , color = colors [solver ])
225
257
ax .set_xlabel ('Time (s)' )
226
258
ax .set_ylabel ('Test accuracy' )
227
259
ax .legend ()
228
260
name = 'single_target' if single_target else 'multi_target'
229
261
name += '_%s' % penalty
230
262
plt .suptitle (name )
231
- name += '.png'
263
+ if outname is None :
264
+ outname = name + '.png'
232
265
fig .tight_layout ()
233
266
fig .subplots_adjust (top = 0.9 )
234
- plt .savefig (name )
235
- plt .close (fig )
267
+
268
+ ax = axes [3 ]
269
+ for scores , times , solver , dtype in zip (group ['train_scores' ],
270
+ group ['times' ],
271
+ group ['solver' ],
272
+ group ["dtype" ]):
273
+ ax .plot (np .arange (len (scores )),
274
+ scores , label = "%s - %s" % (solver , dtype ),
275
+ marker = "." ,
276
+ alpha = alpha [dtype ],
277
+ color = colors [solver ], linestyle = linestyles [dtype ])
278
+
279
+ ax .set_yscale ("log" )
280
+ ax .set_xlabel ('# iterations' )
281
+ ax .set_ylabel ('Objective function' )
282
+ ax .legend ()
283
+
284
+ plt .savefig (outname )
236
285
237
286
238
287
if __name__ == '__main__' :
239
288
solvers = ['saga' , 'liblinear' , 'lightning' ]
240
289
penalties = ['l1' , 'l2' ]
290
+ n_samples = [100000 , 300000 , 500000 , 800000 , None ]
241
291
single_target = True
242
- exp (solvers , penalties , single_target , n_samples = None , n_jobs = 1 ,
243
- dataset = '20newspaper' , max_iter = 20 )
244
- plot ()
292
+ for penalty in penalties :
293
+ for n_sample in n_samples :
294
+ exp (solvers , penalty , single_target ,
295
+ n_samples = n_sample , n_jobs = 1 ,
296
+ dataset = 'rcv1' , max_iter = 10 )
297
+ if n_sample is not None :
298
+ outname = "figures/saga_%s_%d.png" % (penalty , n_sample )
299
+ else :
300
+ outname = "figures/saga_%s_all.png" % (penalty ,)
301
+ try :
302
+ os .makedirs ("figures" )
303
+ except OSError :
304
+ pass
305
+ plot (outname )
0 commit comments