numo-liblinear 0.3.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,88 @@
1
+ #ifndef _LIBLINEAR_H
2
+ #define _LIBLINEAR_H
3
+
4
+ #define LIBLINEAR_VERSION 241
5
+
6
+ #ifdef __cplusplus
7
+ extern "C" {
8
+ #endif
9
+
10
+ extern int liblinear_version;
11
+
12
+ struct feature_node
13
+ {
14
+ int index;
15
+ double value;
16
+ };
17
+
18
+ struct problem
19
+ {
20
+ int l, n;
21
+ double *y;
22
+ struct feature_node **x;
23
+ double bias; /* < 0 if no bias term */
24
+ };
25
+
26
+ enum { L2R_LR, L2R_L2LOSS_SVC_DUAL, L2R_L2LOSS_SVC, L2R_L1LOSS_SVC_DUAL, MCSVM_CS, L1R_L2LOSS_SVC, L1R_LR, L2R_LR_DUAL, L2R_L2LOSS_SVR = 11, L2R_L2LOSS_SVR_DUAL, L2R_L1LOSS_SVR_DUAL, ONECLASS_SVM = 21 }; /* solver_type */
27
+
28
+ struct parameter
29
+ {
30
+ int solver_type;
31
+
32
+ /* these are for training only */
33
+ double eps; /* stopping criteria */
34
+ double C;
35
+ int nr_weight;
36
+ int *weight_label;
37
+ double* weight;
38
+ double p;
39
+ double nu;
40
+ double *init_sol;
41
+ int regularize_bias;
42
+ };
43
+
44
+ struct model
45
+ {
46
+ struct parameter param;
47
+ int nr_class; /* number of classes */
48
+ int nr_feature;
49
+ double *w;
50
+ int *label; /* label of each class */
51
+ double bias;
52
+ double rho; /* one-class SVM only */
53
+ };
54
+
55
+ struct model* train(const struct problem *prob, const struct parameter *param);
56
+ void cross_validation(const struct problem *prob, const struct parameter *param, int nr_fold, double *target);
57
+ void find_parameters(const struct problem *prob, const struct parameter *param, int nr_fold, double start_C, double start_p, double *best_C, double *best_p, double *best_score);
58
+
59
+ double predict_values(const struct model *model_, const struct feature_node *x, double* dec_values);
60
+ double predict(const struct model *model_, const struct feature_node *x);
61
+ double predict_probability(const struct model *model_, const struct feature_node *x, double* prob_estimates);
62
+
63
+ int save_model(const char *model_file_name, const struct model *model_);
64
+ struct model *load_model(const char *model_file_name);
65
+
66
+ int get_nr_feature(const struct model *model_);
67
+ int get_nr_class(const struct model *model_);
68
+ void get_labels(const struct model *model_, int* label);
69
+ double get_decfun_coef(const struct model *model_, int feat_idx, int label_idx);
70
+ double get_decfun_bias(const struct model *model_, int label_idx);
71
+ double get_decfun_rho(const struct model *model_);
72
+
73
+ void free_model_content(struct model *model_ptr);
74
+ void free_and_destroy_model(struct model **model_ptr_ptr);
75
+ void destroy_param(struct parameter *param);
76
+
77
+ const char *check_parameter(const struct problem *prob, const struct parameter *param);
78
+ int check_probability_model(const struct model *model);
79
+ int check_regression_model(const struct model *model);
80
+ int check_oneclass_model(const struct model *model);
81
+ void set_print_string_function(void (*print_func) (const char*));
82
+
83
+ #ifdef __cplusplus
84
+ }
85
+ #endif
86
+
87
+ #endif /* _LIBLINEAR_H */
88
+
@@ -0,0 +1,245 @@
1
+ #include <math.h>
2
+ #include <stdio.h>
3
+ #include <string.h>
4
+ #include <stdarg.h>
5
+ #include "newton.h"
6
+
7
+ #ifndef min
8
+ template <class T> static inline T min(T x,T y) { return (x<y)?x:y; }
9
+ #endif
10
+
11
+ #ifndef max
12
+ template <class T> static inline T max(T x,T y) { return (x>y)?x:y; }
13
+ #endif
14
+
15
+ #ifdef __cplusplus
16
+ extern "C" {
17
+ #endif
18
+
19
+ extern double dnrm2_(int *, double *, int *);
20
+ extern double ddot_(int *, double *, int *, double *, int *);
21
+ extern int daxpy_(int *, double *, double *, int *, double *, int *);
22
+ extern int dscal_(int *, double *, double *, int *);
23
+
24
+ #ifdef __cplusplus
25
+ }
26
+ #endif
27
+
28
+ static void default_print(const char *buf)
29
+ {
30
+ fputs(buf,stdout);
31
+ fflush(stdout);
32
+ }
33
+
34
+ // On entry *f must be the function value of w
35
+ // On exit w is updated and *f is the new function value
36
+ double function::linesearch_and_update(double *w, double *s, double *f, double *g, double alpha)
37
+ {
38
+ double gTs = 0;
39
+ double eta = 0.01;
40
+ int n = get_nr_variable();
41
+ int max_num_linesearch = 20;
42
+ double *w_new = new double[n];
43
+ double fold = *f;
44
+
45
+ for (int i=0;i<n;i++)
46
+ gTs += s[i] * g[i];
47
+
48
+ int num_linesearch = 0;
49
+ for(num_linesearch=0; num_linesearch < max_num_linesearch; num_linesearch++)
50
+ {
51
+ for (int i=0;i<n;i++)
52
+ w_new[i] = w[i] + alpha*s[i];
53
+ *f = fun(w_new);
54
+ if (*f - fold <= eta * alpha * gTs)
55
+ break;
56
+ else
57
+ alpha *= 0.5;
58
+ }
59
+
60
+ if (num_linesearch >= max_num_linesearch)
61
+ {
62
+ *f = fold;
63
+ return 0;
64
+ }
65
+ else
66
+ memcpy(w, w_new, sizeof(double)*n);
67
+
68
+ delete [] w_new;
69
+ return alpha;
70
+ }
71
+
72
+ void NEWTON::info(const char *fmt,...)
73
+ {
74
+ char buf[BUFSIZ];
75
+ va_list ap;
76
+ va_start(ap,fmt);
77
+ vsprintf(buf,fmt,ap);
78
+ va_end(ap);
79
+ (*newton_print_string)(buf);
80
+ }
81
+
82
+ NEWTON::NEWTON(const function *fun_obj, double eps, double eps_cg, int max_iter)
83
+ {
84
+ this->fun_obj=const_cast<function *>(fun_obj);
85
+ this->eps=eps;
86
+ this->eps_cg=eps_cg;
87
+ this->max_iter=max_iter;
88
+ newton_print_string = default_print;
89
+ }
90
+
91
+ NEWTON::~NEWTON()
92
+ {
93
+ }
94
+
95
+ void NEWTON::newton(double *w)
96
+ {
97
+ int n = fun_obj->get_nr_variable();
98
+ int i, cg_iter;
99
+ double step_size;
100
+ double f, fold, actred;
101
+ double init_step_size = 1;
102
+ int search = 1, iter = 1, inc = 1;
103
+ double *s = new double[n];
104
+ double *r = new double[n];
105
+ double *g = new double[n];
106
+
107
+ const double alpha_pcg = 0.01;
108
+ double *M = new double[n];
109
+
110
+ // calculate gradient norm at w=0 for stopping condition.
111
+ double *w0 = new double[n];
112
+ for (i=0; i<n; i++)
113
+ w0[i] = 0;
114
+ fun_obj->fun(w0);
115
+ fun_obj->grad(w0, g);
116
+ double gnorm0 = dnrm2_(&n, g, &inc);
117
+ delete [] w0;
118
+
119
+ f = fun_obj->fun(w);
120
+ info("init f %5.3e\n", f);
121
+ fun_obj->grad(w, g);
122
+ double gnorm = dnrm2_(&n, g, &inc);
123
+
124
+ if (gnorm <= eps*gnorm0)
125
+ search = 0;
126
+
127
+ double *w_new = new double[n];
128
+ while (iter <= max_iter && search)
129
+ {
130
+ fun_obj->get_diag_preconditioner(M);
131
+ for(i=0; i<n; i++)
132
+ M[i] = (1-alpha_pcg) + alpha_pcg*M[i];
133
+ cg_iter = pcg(g, M, s, r);
134
+
135
+ fold = f;
136
+ step_size = fun_obj->linesearch_and_update(w, s, & f, g, init_step_size);
137
+
138
+ if (step_size == 0)
139
+ {
140
+ info("WARNING: line search fails\n");
141
+ break;
142
+ }
143
+
144
+ info("iter %2d f %5.3e |g| %5.3e CG %3d step_size %4.2e \n", iter, f, gnorm, cg_iter, step_size);
145
+
146
+ actred = fold - f;
147
+ iter++;
148
+
149
+ fun_obj->grad(w, g);
150
+
151
+ gnorm = dnrm2_(&n, g, &inc);
152
+ if (gnorm <= eps*gnorm0)
153
+ break;
154
+ if (f < -1.0e+32)
155
+ {
156
+ info("WARNING: f < -1.0e+32\n");
157
+ break;
158
+ }
159
+ if (fabs(actred) <= 1.0e-12*fabs(f))
160
+ {
161
+ info("WARNING: actred too small\n");
162
+ break;
163
+ }
164
+ }
165
+
166
+ delete[] g;
167
+ delete[] r;
168
+ delete[] w_new;
169
+ delete[] s;
170
+ delete[] M;
171
+ }
172
+
173
+ int NEWTON::pcg(double *g, double *M, double *s, double *r)
174
+ {
175
+ int i, inc = 1;
176
+ int n = fun_obj->get_nr_variable();
177
+ double one = 1;
178
+ double *d = new double[n];
179
+ double *Hd = new double[n];
180
+ double zTr, znewTrnew, alpha, beta, cgtol;
181
+ double *z = new double[n];
182
+ double Q = 0, newQ, Qdiff;
183
+
184
+ for (i=0; i<n; i++)
185
+ {
186
+ s[i] = 0;
187
+ r[i] = -g[i];
188
+ z[i] = r[i] / M[i];
189
+ d[i] = z[i];
190
+ }
191
+
192
+ zTr = ddot_(&n, z, &inc, r, &inc);
193
+ double gMinv_norm = sqrt(zTr);
194
+ cgtol = min(eps_cg, sqrt(gMinv_norm));
195
+ int cg_iter = 0;
196
+ int max_cg_iter = max(n, 5);
197
+
198
+ while (cg_iter < max_cg_iter)
199
+ {
200
+ cg_iter++;
201
+ fun_obj->Hv(d, Hd);
202
+
203
+ alpha = zTr/ddot_(&n, d, &inc, Hd, &inc);
204
+ daxpy_(&n, &alpha, d, &inc, s, &inc);
205
+ alpha = -alpha;
206
+ daxpy_(&n, &alpha, Hd, &inc, r, &inc);
207
+
208
+ // Using quadratic approximation as CG stopping criterion
209
+ newQ = -0.5*(ddot_(&n, s, &inc, r, &inc) - ddot_(&n, s, &inc, g, &inc));
210
+ Qdiff = newQ - Q;
211
+ if (newQ <= 0 && Qdiff <= 0)
212
+ {
213
+ if (cg_iter * Qdiff >= cgtol * newQ)
214
+ break;
215
+ }
216
+ else
217
+ {
218
+ info("WARNING: quadratic approximation > 0 or increasing in CG\n");
219
+ break;
220
+ }
221
+ Q = newQ;
222
+
223
+ for (i=0; i<n; i++)
224
+ z[i] = r[i] / M[i];
225
+ znewTrnew = ddot_(&n, z, &inc, r, &inc);
226
+ beta = znewTrnew/zTr;
227
+ dscal_(&n, &beta, d, &inc);
228
+ daxpy_(&n, &one, z, &inc, d, &inc);
229
+ zTr = znewTrnew;
230
+ }
231
+
232
+ if (cg_iter == max_cg_iter)
233
+ info("WARNING: reaching maximal number of CG steps\n");
234
+
235
+ delete[] d;
236
+ delete[] Hd;
237
+ delete[] z;
238
+
239
+ return(cg_iter);
240
+ }
241
+
242
+ void NEWTON::set_print_string(void (*print_string) (const char *buf))
243
+ {
244
+ newton_print_string = print_string;
245
+ }
@@ -0,0 +1,37 @@
1
+ #ifndef _NEWTON_H
2
+ #define _NEWTON_H
3
+
4
+ class function
5
+ {
6
+ public:
7
+ virtual double fun(double *w) = 0 ;
8
+ virtual void grad(double *w, double *g) = 0 ;
9
+ virtual void Hv(double *s, double *Hs) = 0 ;
10
+ virtual int get_nr_variable(void) = 0 ;
11
+ virtual void get_diag_preconditioner(double *M) = 0 ;
12
+ virtual ~function(void){}
13
+
14
+ // base implementation in newton.cpp
15
+ virtual double linesearch_and_update(double *w, double *s, double *f, double *g, double alpha);
16
+ };
17
+
18
+ class NEWTON
19
+ {
20
+ public:
21
+ NEWTON(const function *fun_obj, double eps = 0.1, double eps_cg = 0.5, int max_iter = 1000);
22
+ ~NEWTON();
23
+
24
+ void newton(double *w);
25
+ void set_print_string(void (*i_print) (const char *buf));
26
+
27
+ private:
28
+ int pcg(double *g, double *M, double *s, double *r);
29
+
30
+ double eps;
31
+ double eps_cg;
32
+ int max_iter;
33
+ function *fun_obj;
34
+ void info(const char *fmt,...);
35
+ void (*newton_print_string)(const char *buf);
36
+ };
37
+ #endif
@@ -53,6 +53,7 @@ VALUE numo_liblinear_train(VALUE self, VALUE x_val, VALUE y_val, VALUE param_has
53
53
  narray_t* y_nary;
54
54
  char* err_msg;
55
55
  VALUE random_seed;
56
+ VALUE verbose;
56
57
  VALUE model_hash;
57
58
 
58
59
  if (CLASS_OF(x_val) != numo_cDFloat) {
@@ -99,7 +100,11 @@ VALUE numo_liblinear_train(VALUE self, VALUE x_val, VALUE y_val, VALUE param_has
99
100
  return Qnil;
100
101
  }
101
102
 
102
- set_print_string_function(print_null);
103
+ verbose = rb_hash_aref(param_hash, ID2SYM(rb_intern("verbose")));
104
+ if (verbose != Qtrue) {
105
+ set_print_string_function(print_null);
106
+ }
107
+
103
108
  model = train(problem, param);
104
109
  model_hash = model_to_rb_hash(model);
105
110
  free_and_destroy_model(&model);
@@ -107,6 +112,9 @@ VALUE numo_liblinear_train(VALUE self, VALUE x_val, VALUE y_val, VALUE param_has
107
112
  xfree_problem(problem);
108
113
  xfree_parameter(param);
109
114
 
115
+ RB_GC_GUARD(x_val);
116
+ RB_GC_GUARD(y_val);
117
+
110
118
  return model_hash;
111
119
  }
112
120
 
@@ -120,6 +128,28 @@ VALUE numo_liblinear_train(VALUE self, VALUE x_val, VALUE y_val, VALUE param_has
120
128
  * @param param [Hash] The parameters of a model.
121
129
  * @param n_folds [Integer] The number of folds.
122
130
  *
131
+ * @example
132
+ * require 'numo/liblinear'
133
+ *
134
+ * # x: samples
135
+ * # y: labels
136
+ *
137
+ * # Define parameters of L2-regularized L2-loss support vector classification.
138
+ * param = {
139
+ * solver_type: Numo::Liblinear::SolverType::L2R_L2LOSS_SVC_DUAL,
140
+ * C: 1,
141
+ * random_seed: 1,
142
+ * verbose: true
143
+ * }
144
+ *
145
+ * # Perform 5-cross validation.
146
+ * n_folds = 5
147
+ * res = Numo::Liblinear::cv(x, y, param, n_folds)
148
+ *
149
+ * # Print mean accuracy.
150
+ * mean_accuracy = y.eq(res).count.fdiv(y.size)
151
+ * puts "Accuracy: %.1f %%" % (100 * mean_accuracy)
152
+ *
123
153
  * @raise [ArgumentError] If the sample array is not 2-dimensional, the label array is not 1-dimensional,
124
154
  * the sample array and label array do not have the same number of samples, or
125
155
  * the hyperparameter has an invalid value, this error is raised.
@@ -136,6 +166,7 @@ VALUE numo_liblinear_cross_validation(VALUE self, VALUE x_val, VALUE y_val, VALU
136
166
  narray_t* y_nary;
137
167
  char* err_msg;
138
168
  VALUE random_seed;
169
+ VALUE verbose;
139
170
  struct problem* problem;
140
171
  struct parameter* param;
141
172
 
@@ -187,12 +218,19 @@ VALUE numo_liblinear_cross_validation(VALUE self, VALUE x_val, VALUE y_val, VALU
187
218
  t_val = rb_narray_new(numo_cDFloat, 1, t_shape);
188
219
  t_pt = (double*)na_get_pointer_for_write(t_val);
189
220
 
190
- set_print_string_function(print_null);
221
+ verbose = rb_hash_aref(param_hash, ID2SYM(rb_intern("verbose")));
222
+ if (verbose != Qtrue) {
223
+ set_print_string_function(print_null);
224
+ }
225
+
191
226
  cross_validation(problem, param, n_folds, t_pt);
192
227
 
193
228
  xfree_problem(problem);
194
229
  xfree_parameter(param);
195
230
 
231
+ RB_GC_GUARD(x_val);
232
+ RB_GC_GUARD(y_val);
233
+
196
234
  return t_val;
197
235
  }
198
236
 
@@ -250,21 +288,17 @@ VALUE numo_liblinear_predict(VALUE self, VALUE x_val, VALUE param_hash, VALUE mo
250
288
  x_pt = (double*)na_get_pointer_for_read(x_val);
251
289
 
252
290
  /* Predict values. */
253
- x_nodes = ALLOC_N(struct feature_node, n_features + 1);
254
- x_nodes[n_features].index = -1;
255
- x_nodes[n_features].value = 0.0;
256
291
  for (i = 0; i < n_samples; i++) {
257
- for (j = 0; j < n_features; j++) {
258
- x_nodes[j].index = j + 1;
259
- x_nodes[j].value = (double)x_pt[i * n_features + j];
260
- }
292
+ x_nodes = dbl_vec_to_node(&x_pt[i * n_features], n_features);
261
293
  y_pt[i] = predict(model, x_nodes);
294
+ xfree(x_nodes);
262
295
  }
263
296
 
264
- xfree(x_nodes);
265
297
  xfree_model(model);
266
298
  xfree_parameter(param);
267
299
 
300
+ RB_GC_GUARD(x_val);
301
+
268
302
  return y_val;
269
303
  }
270
304
 
@@ -333,40 +367,30 @@ VALUE numo_liblinear_decision_function(VALUE self, VALUE x_val, VALUE param_hash
333
367
 
334
368
  /* Predict values. */
335
369
  if (model->nr_class == 2 && model->param.solver_type != MCSVM_CS) {
336
- x_nodes = ALLOC_N(struct feature_node, n_features + 1);
337
- x_nodes[n_features].index = -1;
338
- x_nodes[n_features].value = 0.0;
339
370
  for (i = 0; i < n_samples; i++) {
340
- for (j = 0; j < n_features; j++) {
341
- x_nodes[j].index = j + 1;
342
- x_nodes[j].value = (double)x_pt[i * n_features + j];
343
- }
371
+ x_nodes = dbl_vec_to_node(&x_pt[i * n_features], n_features);
344
372
  predict_values(model, x_nodes, &y_pt[i]);
373
+ xfree(x_nodes);
345
374
  }
346
- xfree(x_nodes);
347
375
  } else {
348
376
  y_cols = (int)y_shape[1];
349
377
  dec_values = ALLOC_N(double, y_cols);
350
- x_nodes = ALLOC_N(struct feature_node, n_features + 1);
351
- x_nodes[n_features].index = -1;
352
- x_nodes[n_features].value = 0.0;
353
378
  for (i = 0; i < n_samples; i++) {
354
- for (j = 0; j < n_features; j++) {
355
- x_nodes[j].index = j + 1;
356
- x_nodes[j].value = (double)x_pt[i * n_features + j];
357
- }
379
+ x_nodes = dbl_vec_to_node(&x_pt[i * n_features], n_features);
358
380
  predict_values(model, x_nodes, dec_values);
381
+ xfree(x_nodes);
359
382
  for (j = 0; j < y_cols; j++) {
360
383
  y_pt[i * y_cols + j] = dec_values[j];
361
384
  }
362
385
  }
363
- xfree(x_nodes);
364
386
  xfree(dec_values);
365
387
  }
366
388
 
367
389
  xfree_model(model);
368
390
  xfree_parameter(param);
369
391
 
392
+ RB_GC_GUARD(x_val);
393
+
370
394
  return y_val;
371
395
  }
372
396
 
@@ -429,26 +453,22 @@ VALUE numo_liblinear_predict_proba(VALUE self, VALUE x_val, VALUE param_hash, VA
429
453
 
430
454
  /* Predict values. */
431
455
  probs = ALLOC_N(double, model->nr_class);
432
- x_nodes = ALLOC_N(struct feature_node, n_features + 1);
433
- x_nodes[n_features].index = -1;
434
- x_nodes[n_features].value = 0.0;
435
456
  for (i = 0; i < n_samples; i++) {
436
- for (j = 0; j < n_features; j++) {
437
- x_nodes[j].index = j + 1;
438
- x_nodes[j].value = (double)x_pt[i * n_features + j];
439
- }
457
+ x_nodes = dbl_vec_to_node(&x_pt[i * n_features], n_features);
440
458
  predict_probability(model, x_nodes, probs);
459
+ xfree(x_nodes);
441
460
  for (j = 0; j < model->nr_class; j++) {
442
461
  y_pt[i * model->nr_class + j] = probs[j];
443
462
  }
444
463
  }
445
- xfree(x_nodes);
446
464
  xfree(probs);
447
465
  }
448
466
 
449
467
  xfree_model(model);
450
468
  xfree_parameter(param);
451
469
 
470
+ RB_GC_GUARD(x_val);
471
+
452
472
  return y_val;
453
473
  }
454
474
 
@@ -537,6 +557,9 @@ void Init_liblinearext()
537
557
  */
538
558
  mLiblinear = rb_define_module_under(mNumo, "Liblinear");
539
559
 
560
+ /* The version of LIBLINEAR used in backgroud library. */
561
+ rb_define_const(mLiblinear, "LIBLINEAR_VERSION", INT2NUM(LIBLINEAR_VERSION));
562
+
540
563
  rb_define_module_function(mLiblinear, "train", numo_liblinear_train, 3);
541
564
  rb_define_module_function(mLiblinear, "cv", numo_liblinear_cross_validation, 4);
542
565
  rb_define_module_function(mLiblinear, "predict", numo_liblinear_predict, 3);