numo-libsvm 0.3.0 → 1.0.2

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.
@@ -0,0 +1,104 @@
1
+ #ifndef _LIBSVM_H
2
+ #define _LIBSVM_H
3
+
4
+ #define LIBSVM_VERSION 324
5
+
6
+ #ifdef __cplusplus
7
+ extern "C" {
8
+ #endif
9
+
10
+ extern int libsvm_version;
11
+
12
+ struct svm_node
13
+ {
14
+ int index;
15
+ double value;
16
+ };
17
+
18
+ struct svm_problem
19
+ {
20
+ int l;
21
+ double *y;
22
+ struct svm_node **x;
23
+ };
24
+
25
+ enum { C_SVC, NU_SVC, ONE_CLASS, EPSILON_SVR, NU_SVR }; /* svm_type */
26
+ enum { LINEAR, POLY, RBF, SIGMOID, PRECOMPUTED }; /* kernel_type */
27
+
28
+ struct svm_parameter
29
+ {
30
+ int svm_type;
31
+ int kernel_type;
32
+ int degree; /* for poly */
33
+ double gamma; /* for poly/rbf/sigmoid */
34
+ double coef0; /* for poly/sigmoid */
35
+
36
+ /* these are for training only */
37
+ double cache_size; /* in MB */
38
+ double eps; /* stopping criteria */
39
+ double C; /* for C_SVC, EPSILON_SVR and NU_SVR */
40
+ int nr_weight; /* for C_SVC */
41
+ int *weight_label; /* for C_SVC */
42
+ double* weight; /* for C_SVC */
43
+ double nu; /* for NU_SVC, ONE_CLASS, and NU_SVR */
44
+ double p; /* for EPSILON_SVR */
45
+ int shrinking; /* use the shrinking heuristics */
46
+ int probability; /* do probability estimates */
47
+ };
48
+
49
+ //
50
+ // svm_model
51
+ //
52
+ struct svm_model
53
+ {
54
+ struct svm_parameter param; /* parameter */
55
+ int nr_class; /* number of classes, = 2 in regression/one class svm */
56
+ int l; /* total #SV */
57
+ struct svm_node **SV; /* SVs (SV[l]) */
58
+ double **sv_coef; /* coefficients for SVs in decision functions (sv_coef[k-1][l]) */
59
+ double *rho; /* constants in decision functions (rho[k*(k-1)/2]) */
60
+ double *probA; /* pariwise probability information */
61
+ double *probB;
62
+ int *sv_indices; /* sv_indices[0,...,nSV-1] are values in [1,...,num_traning_data] to indicate SVs in the training set */
63
+
64
+ /* for classification only */
65
+
66
+ int *label; /* label of each class (label[k]) */
67
+ int *nSV; /* number of SVs for each class (nSV[k]) */
68
+ /* nSV[0] + nSV[1] + ... + nSV[k-1] = l */
69
+ /* XXX */
70
+ int free_sv; /* 1 if svm_model is created by svm_load_model*/
71
+ /* 0 if svm_model is created by svm_train */
72
+ };
73
+
74
+ struct svm_model *svm_train(const struct svm_problem *prob, const struct svm_parameter *param);
75
+ void svm_cross_validation(const struct svm_problem *prob, const struct svm_parameter *param, int nr_fold, double *target);
76
+
77
+ int svm_save_model(const char *model_file_name, const struct svm_model *model);
78
+ struct svm_model *svm_load_model(const char *model_file_name);
79
+
80
+ int svm_get_svm_type(const struct svm_model *model);
81
+ int svm_get_nr_class(const struct svm_model *model);
82
+ void svm_get_labels(const struct svm_model *model, int *label);
83
+ void svm_get_sv_indices(const struct svm_model *model, int *sv_indices);
84
+ int svm_get_nr_sv(const struct svm_model *model);
85
+ double svm_get_svr_probability(const struct svm_model *model);
86
+
87
+ double svm_predict_values(const struct svm_model *model, const struct svm_node *x, double* dec_values);
88
+ double svm_predict(const struct svm_model *model, const struct svm_node *x);
89
+ double svm_predict_probability(const struct svm_model *model, const struct svm_node *x, double* prob_estimates);
90
+
91
+ void svm_free_model_content(struct svm_model *model_ptr);
92
+ void svm_free_and_destroy_model(struct svm_model **model_ptr_ptr);
93
+ void svm_destroy_param(struct svm_parameter *param);
94
+
95
+ const char *svm_check_parameter(const struct svm_problem *prob, const struct svm_parameter *param);
96
+ int svm_check_probability_model(const struct svm_model *model);
97
+
98
+ void svm_set_print_string_function(void (*print_func)(const char *));
99
+
100
+ #ifdef __cplusplus
101
+ }
102
+ #endif
103
+
104
+ #endif /* _LIBSVM_H */
@@ -55,6 +55,7 @@ VALUE train(VALUE self, VALUE x_val, VALUE y_val, VALUE param_hash)
55
55
  narray_t* y_nary;
56
56
  char* err_msg;
57
57
  VALUE random_seed;
58
+ VALUE verbose;
58
59
  VALUE model_hash;
59
60
 
60
61
  if (CLASS_OF(x_val) != numo_cDFloat) {
@@ -101,7 +102,11 @@ VALUE train(VALUE self, VALUE x_val, VALUE y_val, VALUE param_hash)
101
102
  return Qnil;
102
103
  }
103
104
 
104
- svm_set_print_string_function(print_null);
105
+ verbose = rb_hash_aref(param_hash, ID2SYM(rb_intern("verbose")));
106
+ if (verbose != Qtrue) {
107
+ svm_set_print_string_function(print_null);
108
+ }
109
+
105
110
  model = svm_train(problem, param);
106
111
  model_hash = svm_model_to_rb_hash(model);
107
112
  svm_free_and_destroy_model(&model);
@@ -109,6 +114,9 @@ VALUE train(VALUE self, VALUE x_val, VALUE y_val, VALUE param_hash)
109
114
  xfree_svm_problem(problem);
110
115
  xfree_svm_parameter(param);
111
116
 
117
+ RB_GC_GUARD(x_val);
118
+ RB_GC_GUARD(y_val);
119
+
112
120
  return model_hash;
113
121
  }
114
122
 
@@ -122,6 +130,30 @@ VALUE train(VALUE self, VALUE x_val, VALUE y_val, VALUE param_hash)
122
130
  * @param param [Hash] The parameters of an SVM model.
123
131
  * @param n_folds [Integer] The number of folds.
124
132
  *
133
+ * @example
134
+ * require 'numo/libsvm'
135
+ *
136
+ * # x: samples
137
+ * # y: labels
138
+ *
139
+ * # Define parameters of C-SVC with RBF Kernel.
140
+ * param = {
141
+ * svm_type: Numo::Libsvm::SvmType::C_SVC,
142
+ * kernel_type: Numo::Libsvm::KernelType::RBF,
143
+ * gamma: 1.0,
144
+ * C: 1,
145
+ * random_seed: 1,
146
+ * verbose: true
147
+ * }
148
+ *
149
+ * # Perform 5-cross validation.
150
+ * n_folds = 5
151
+ * res = Numo::Libsvm.cv(x, y, param, n_folds)
152
+ *
153
+ * # Print mean accuracy.
154
+ * mean_accuracy = y.eq(res).count.fdiv(y.size)
155
+ * puts "Accuracy: %.1f %%" % (100 * mean_accuracy)
156
+ *
125
157
  * @raise [ArgumentError] If the sample array is not 2-dimensional, the label array is not 1-dimensional,
126
158
  * the sample array and label array do not have the same number of samples, or
127
159
  * the hyperparameter has an invalid value, this error is raised.
@@ -138,6 +170,7 @@ VALUE cross_validation(VALUE self, VALUE x_val, VALUE y_val, VALUE param_hash, V
138
170
  narray_t* y_nary;
139
171
  char* err_msg;
140
172
  VALUE random_seed;
173
+ VALUE verbose;
141
174
  struct svm_problem* problem;
142
175
  struct svm_parameter* param;
143
176
 
@@ -189,12 +222,19 @@ VALUE cross_validation(VALUE self, VALUE x_val, VALUE y_val, VALUE param_hash, V
189
222
  t_val = rb_narray_new(numo_cDFloat, 1, t_shape);
190
223
  t_pt = (double*)na_get_pointer_for_write(t_val);
191
224
 
192
- svm_set_print_string_function(print_null);
225
+ verbose = rb_hash_aref(param_hash, ID2SYM(rb_intern("verbose")));
226
+ if (verbose != Qtrue) {
227
+ svm_set_print_string_function(print_null);
228
+ }
229
+
193
230
  svm_cross_validation(problem, param, n_folds, t_pt);
194
231
 
195
232
  xfree_svm_problem(problem);
196
233
  xfree_svm_parameter(param);
197
234
 
235
+ RB_GC_GUARD(x_val);
236
+ RB_GC_GUARD(y_val);
237
+
198
238
  return t_val;
199
239
  }
200
240
 
@@ -220,9 +260,10 @@ VALUE predict(VALUE self, VALUE x_val, VALUE param_hash, VALUE model_hash)
220
260
  size_t y_shape[1];
221
261
  VALUE y_val;
222
262
  double* y_pt;
223
- int i, j;
263
+ int i, j, k;
224
264
  int n_samples;
225
265
  int n_features;
266
+ int n_nonzero_features;
226
267
 
227
268
  /* Obtain C data structures. */
228
269
  if (CLASS_OF(x_val) != numo_cDFloat) {
@@ -251,21 +292,17 @@ VALUE predict(VALUE self, VALUE x_val, VALUE param_hash, VALUE model_hash)
251
292
  x_pt = (double*)na_get_pointer_for_read(x_val);
252
293
 
253
294
  /* Predict values. */
254
- x_nodes = ALLOC_N(struct svm_node, n_features + 1);
255
- x_nodes[n_features].index = -1;
256
- x_nodes[n_features].value = 0.0;
257
295
  for (i = 0; i < n_samples; i++) {
258
- for (j = 0; j < n_features; j++) {
259
- x_nodes[j].index = j + 1;
260
- x_nodes[j].value = (double)x_pt[i * n_features + j];
261
- }
296
+ x_nodes = dbl_vec_to_svm_node(&x_pt[i * n_features], n_features);
262
297
  y_pt[i] = svm_predict(model, x_nodes);
298
+ xfree(x_nodes);
263
299
  }
264
300
 
265
- xfree(x_nodes);
266
301
  xfree_svm_model(model);
267
302
  xfree_svm_parameter(param);
268
303
 
304
+ RB_GC_GUARD(x_val);
305
+
269
306
  return y_val;
270
307
  }
271
308
 
@@ -334,40 +371,30 @@ VALUE decision_function(VALUE self, VALUE x_val, VALUE param_hash, VALUE model_h
334
371
 
335
372
  /* Predict values. */
336
373
  if (model->param.svm_type == ONE_CLASS || model->param.svm_type == EPSILON_SVR || model->param.svm_type == NU_SVR) {
337
- x_nodes = ALLOC_N(struct svm_node, n_features + 1);
338
- x_nodes[n_features].index = -1;
339
- x_nodes[n_features].value = 0.0;
340
374
  for (i = 0; i < n_samples; i++) {
341
- for (j = 0; j < n_features; j++) {
342
- x_nodes[j].index = j + 1;
343
- x_nodes[j].value = (double)x_pt[i * n_features + j];
344
- }
375
+ x_nodes = dbl_vec_to_svm_node(&x_pt[i * n_features], n_features);
345
376
  svm_predict_values(model, x_nodes, &y_pt[i]);
377
+ xfree(x_nodes);
346
378
  }
347
- xfree(x_nodes);
348
379
  } else {
349
380
  y_cols = (int)y_shape[1];
350
381
  dec_values = ALLOC_N(double, y_cols);
351
- x_nodes = ALLOC_N(struct svm_node, n_features + 1);
352
- x_nodes[n_features].index = -1;
353
- x_nodes[n_features].value = 0.0;
354
382
  for (i = 0; i < n_samples; i++) {
355
- for (j = 0; j < n_features; j++) {
356
- x_nodes[j].index = j + 1;
357
- x_nodes[j].value = (double)x_pt[i * n_features + j];
358
- }
383
+ x_nodes = dbl_vec_to_svm_node(&x_pt[i * n_features], n_features);
359
384
  svm_predict_values(model, x_nodes, dec_values);
385
+ xfree(x_nodes);
360
386
  for (j = 0; j < y_cols; j++) {
361
387
  y_pt[i * y_cols + j] = dec_values[j];
362
388
  }
363
389
  }
364
- xfree(x_nodes);
365
390
  xfree(dec_values);
366
391
  }
367
392
 
368
393
  xfree_svm_model(model);
369
394
  xfree_svm_parameter(param);
370
395
 
396
+ RB_GC_GUARD(x_val);
397
+
371
398
  return y_val;
372
399
  }
373
400
 
@@ -429,26 +456,22 @@ VALUE predict_proba(VALUE self, VALUE x_val, VALUE param_hash, VALUE model_hash)
429
456
 
430
457
  /* Predict values. */
431
458
  probs = ALLOC_N(double, model->nr_class);
432
- x_nodes = ALLOC_N(struct svm_node, n_features + 1);
433
- x_nodes[n_features].index = -1;
434
- x_nodes[n_features].value = 0.0;
435
459
  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
- }
460
+ x_nodes = dbl_vec_to_svm_node(&x_pt[i * n_features], n_features);
440
461
  svm_predict_probability(model, x_nodes, probs);
462
+ xfree(x_nodes);
441
463
  for (j = 0; j < model->nr_class; j++) {
442
464
  y_pt[i * model->nr_class + j] = probs[j];
443
465
  }
444
466
  }
445
- xfree(x_nodes);
446
467
  xfree(probs);
447
468
  }
448
469
 
449
470
  xfree_svm_model(model);
450
471
  xfree_svm_parameter(param);
451
472
 
473
+ RB_GC_GUARD(x_val);
474
+
452
475
  return y_val;
453
476
  }
454
477
 
@@ -482,6 +505,8 @@ VALUE load_svm_model(VALUE self, VALUE filename)
482
505
  rb_ary_store(res, 0, param_hash);
483
506
  rb_ary_store(res, 1, model_hash);
484
507
 
508
+ RB_GC_GUARD(filename);
509
+
485
510
  return res;
486
511
  }
487
512
 
@@ -516,6 +541,8 @@ VALUE save_svm_model(VALUE self, VALUE filename, VALUE param_hash, VALUE model_h
516
541
  return Qfalse;
517
542
  }
518
543
 
544
+ RB_GC_GUARD(filename);
545
+
519
546
  return Qtrue;
520
547
  }
521
548
 
@@ -35,13 +35,13 @@ struct svm_parameter* rb_hash_to_svm_parameter(VALUE param_hash)
35
35
  param->weight_label = NULL;
36
36
  if (!NIL_P(el)) {
37
37
  param->weight_label = ALLOC_N(int, param->nr_weight);
38
- memcpy(param->weight_label, (int32_t*)na_get_pointer_for_read(el), param->nr_weight);
38
+ memcpy(param->weight_label, (int32_t*)na_get_pointer_for_read(el), param->nr_weight * sizeof(int32_t));
39
39
  }
40
40
  el = rb_hash_aref(param_hash, ID2SYM(rb_intern("weight")));
41
41
  param->weight = NULL;
42
42
  if (!NIL_P(el)) {
43
43
  param->weight = ALLOC_N(double, param->nr_weight);
44
- memcpy(param->weight, (double*)na_get_pointer_for_read(el), param->nr_weight);
44
+ memcpy(param->weight, (double*)na_get_pointer_for_read(el), param->nr_weight * sizeof(double));
45
45
  }
46
46
  return param;
47
47
  }
@@ -29,9 +29,12 @@ struct svm_problem* dataset_to_svm_problem(VALUE x_val, VALUE y_val)
29
29
  narray_t* x_nary;
30
30
  double* x_pt;
31
31
  double* y_pt;
32
- int i, j;
32
+ int i, j, k;
33
33
  int n_samples;
34
34
  int n_features;
35
+ int n_nonzero_features;
36
+ int is_padded;
37
+ int last_feature_id;
35
38
 
36
39
  GetNArray(x_val, x_nary);
37
40
  n_samples = (int)NA_SHAPE(x_nary)[0];
@@ -43,16 +46,45 @@ struct svm_problem* dataset_to_svm_problem(VALUE x_val, VALUE y_val)
43
46
  problem->l = n_samples;
44
47
  problem->x = ALLOC_N(struct svm_node*, n_samples);
45
48
  problem->y = ALLOC_N(double, n_samples);
49
+
50
+ is_padded = 0;
46
51
  for (i = 0; i < n_samples; i++) {
47
- problem->x[i] = ALLOC_N(struct svm_node, n_features + 1);
52
+ n_nonzero_features = 0;
48
53
  for (j = 0; j < n_features; j++) {
49
- problem->x[i][j].index = j + 1;
50
- problem->x[i][j].value = x_pt[i * n_features + j];
54
+ if (x_pt[i * n_features + j] != 0.0) {
55
+ n_nonzero_features += 1;
56
+ last_feature_id = j + 1;
57
+ }
58
+ }
59
+ if (is_padded == 0 && last_feature_id == n_features) {
60
+ is_padded = 1;
61
+ }
62
+ if (is_padded == 1) {
63
+ problem->x[i] = ALLOC_N(struct svm_node, n_nonzero_features + 1);
64
+ } else {
65
+ problem->x[i] = ALLOC_N(struct svm_node, n_nonzero_features + 2);
66
+ }
67
+ for (j = 0, k = 0; j < n_features; j++) {
68
+ if (x_pt[i * n_features + j] != 0.0) {
69
+ problem->x[i][k].index = j + 1;
70
+ problem->x[i][k].value = (double)x_pt[i * n_features + j];
71
+ k++;
72
+ }
73
+ }
74
+ if (is_padded == 1) {
75
+ problem->x[i][n_nonzero_features].index = -1;
76
+ problem->x[i][n_nonzero_features].value = 0.0;
77
+ } else {
78
+ problem->x[i][n_nonzero_features].index = n_features;
79
+ problem->x[i][n_nonzero_features].value = 0.0;
80
+ problem->x[i][n_nonzero_features + 1].index = -1;
81
+ problem->x[i][n_nonzero_features + 1].value = 0.0;
51
82
  }
52
- problem->x[i][n_features].index = -1;
53
- problem->x[i][n_features].value = 0.0;
54
83
  problem->y[i] = y_pt[i];
55
84
  }
56
85
 
86
+ RB_GC_GUARD(x_val);
87
+ RB_GC_GUARD(y_val);
88
+
57
89
  return problem;
58
90
  }
@@ -3,6 +3,6 @@
3
3
  module Numo
4
4
  module Libsvm
5
5
  # The version of Numo::Libsvm you are using.
6
- VERSION = '0.3.0'
6
+ VERSION = '1.0.2'
7
7
  end
8
8
  end
@@ -28,14 +28,28 @@ Gem::Specification.new do |spec|
28
28
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
29
29
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
30
30
  end
31
+
32
+ gem_dir = File.expand_path(__dir__) + '/'
33
+ submodule_path = `git submodule --quiet foreach pwd`.split($OUTPUT_RECORD_SEPARATOR).first
34
+ submodule_relative_path = submodule_path.sub gem_dir, ''
35
+ spec.files << "#{submodule_relative_path}/svm.cpp"
36
+ spec.files << "#{submodule_relative_path}/svm.h"
37
+
31
38
  spec.bindir = 'exe'
32
39
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
33
40
  spec.require_paths = ['lib']
34
41
  spec.extensions = ['ext/numo/libsvm/extconf.rb']
35
42
 
43
+ spec.metadata = {
44
+ 'homepage_uri' => 'https://github.com/yoshoku/numo-libsvm',
45
+ 'source_code_uri' => 'https://github.com/yoshoku/numo-libsvm',
46
+ 'documentation_uri' => 'https://yoshoku.github.io/numo-libsvm/doc/'
47
+ }
48
+
36
49
  spec.add_runtime_dependency 'numo-narray', '~> 0.9.1'
50
+
37
51
  spec.add_development_dependency 'bundler', '~> 2.0'
38
- spec.add_development_dependency 'rake', '~> 10.0'
52
+ spec.add_development_dependency 'rake', '~> 12.0'
39
53
  spec.add_development_dependency 'rake-compiler', '~> 1.0'
40
54
  spec.add_development_dependency 'rspec', '~> 3.0'
41
55
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: numo-libsvm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - yoshoku
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-08-22 00:00:00.000000000 Z
11
+ date: 2021-01-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: numo-narray
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '10.0'
47
+ version: '12.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '10.0'
54
+ version: '12.0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake-compiler
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -92,9 +92,10 @@ extensions:
92
92
  - ext/numo/libsvm/extconf.rb
93
93
  extra_rdoc_files: []
94
94
  files:
95
+ - ".github/workflows/build.yml"
95
96
  - ".gitignore"
97
+ - ".gitmodules"
96
98
  - ".rspec"
97
- - ".travis.yml"
98
99
  - CHANGELOG.md
99
100
  - CODE_OF_CONDUCT.md
100
101
  - Gemfile
@@ -106,6 +107,8 @@ files:
106
107
  - ext/numo/libsvm/extconf.rb
107
108
  - ext/numo/libsvm/kernel_type.c
108
109
  - ext/numo/libsvm/kernel_type.h
110
+ - ext/numo/libsvm/libsvm/svm.cpp
111
+ - ext/numo/libsvm/libsvm/svm.h
109
112
  - ext/numo/libsvm/libsvmext.c
110
113
  - ext/numo/libsvm/libsvmext.h
111
114
  - ext/numo/libsvm/svm_model.c
@@ -122,8 +125,11 @@ files:
122
125
  homepage: https://github.com/yoshoku/numo-libsvm
123
126
  licenses:
124
127
  - BSD-3-Clause
125
- metadata: {}
126
- post_install_message:
128
+ metadata:
129
+ homepage_uri: https://github.com/yoshoku/numo-libsvm
130
+ source_code_uri: https://github.com/yoshoku/numo-libsvm
131
+ documentation_uri: https://yoshoku.github.io/numo-libsvm/doc/
132
+ post_install_message:
127
133
  rdoc_options: []
128
134
  require_paths:
129
135
  - lib
@@ -138,9 +144,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
144
  - !ruby/object:Gem::Version
139
145
  version: '0'
140
146
  requirements: []
141
- rubyforge_project:
142
- rubygems_version: 2.6.14.4
143
- signing_key:
147
+ rubygems_version: 3.2.3
148
+ signing_key:
144
149
  specification_version: 4
145
150
  summary: Numo::Libsvm is a Ruby gem binding to the LIBSVM library. Numo::Libsvm makes
146
151
  to use the LIBSVM functions with dataset represented by Numo::NArray.