rumale 0.21.0 → 0.22.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fd5ca16629a5258be9e577771dc8c6b42dbfdf3a60a4c43d4ee170cc17b72bea
4
- data.tar.gz: 70b26bcf0b39bb8e716b9bbb4aba100c496152b1c4e71879105046440c7d8758
3
+ metadata.gz: 4e2f68b3182ada73537901e7bc74bddd100aff75264f9147c88d8240fb624e29
4
+ data.tar.gz: e2639a55fc84d1399b925f65b3a56b38f2ae3150dd15ab8556120af28d408cae
5
5
  SHA512:
6
- metadata.gz: 645a6bda6e3601534c69f5ecfbd840c1d6c1ed7a5a3b8bd57995621a03d970cd02e9749a8a70be5af2678c029a26a5e6e1c32376a4514a64e96d6a9b4b12aa3e
7
- data.tar.gz: 5904c64da9cc30cf0c288dfbeb3051bca3333e588e153cf19619c169d713e93edcb95e6902134e58c823d621ebad9e6a56310123c110b6d810033f5f96a40fbb
6
+ metadata.gz: 91ffcbade578bbb9c6a5d87a54ebd89a2b5990eb70835e7a5549afe78541dbfeafe3af50833725bee751fa89c059484970e5add7ebf8adee3e25bc000fbe3778
7
+ data.tar.gz: 2ee2b1448a486581ef98561f65bc3446b2e161c89a3a12bd6cd78867350e26151bc0b350bd431902d21f6979493ab2d01a6ee81b55c1099f631aa84c84a704e6
@@ -0,0 +1,23 @@
1
+ name: build
2
+
3
+ on: [push]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ matrix:
10
+ ruby: [ '2.5', '2.6', '2.7' ]
11
+ steps:
12
+ - uses: actions/checkout@v2
13
+ - name: Install BLAS and LAPACK
14
+ run: sudo apt-get install -y libopenblas-dev liblapacke-dev
15
+ - name: Set up Ruby ${{ matrix.ruby }}
16
+ uses: actions/setup-ruby@v1
17
+ with:
18
+ ruby-version: ${{ matrix.ruby }}
19
+ - name: Build and test with Rake
20
+ run: |
21
+ gem install bundler
22
+ bundle install --jobs 4 --retry 3
23
+ bundle exec rake
@@ -1,3 +1,9 @@
1
+ # 0.22.0
2
+ ## Breaking change
3
+ - Add lbfgsb.rb gem to runtime dependencies. Rumale uses lbfgsb gem for optimization.
4
+ This eliminates the need to require the mopti gem when using [NeighbourhoodComponentAnalysis](https://yoshoku.github.io/rumale/doc/Rumale/MetricLearning/NeighbourhoodComponentAnalysis.html).
5
+ - Add lbfgs solver to [LogisticRegression](https://yoshoku.github.io/rumale/doc/Rumale/LinearModel/LogisticRegression.html) and make it the default solver.
6
+
1
7
  # 0.21.0
2
8
  ## Breaking change
3
9
  - Change the default value of max_iter argument on LinearModel estimators to 1000.
data/Gemfile CHANGED
@@ -3,9 +3,7 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in rumale.gemspec
4
4
  gemspec
5
5
 
6
- gem 'coveralls', '~> 0.8'
7
6
  gem 'mmh3', '>= 1.0'
8
- gem 'mopti', '>= 0.1.0'
9
7
  gem 'numo-linalg', '>= 0.1.4'
10
8
  gem 'parallel', '>= 1.17.0'
11
9
  gem 'rake', '~> 12.0'
@@ -14,3 +12,4 @@ gem 'rspec', '~> 3.0'
14
12
  gem 'rubocop', '~> 0.91'
15
13
  gem 'rubocop-performance', '~> 1.8'
16
14
  gem 'rubocop-rspec', '~> 1.43'
15
+ gem 'simplecov', '~> 0.19'
data/README.md CHANGED
@@ -2,8 +2,7 @@
2
2
 
3
3
  ![Rumale](https://dl.dropboxusercontent.com/s/joxruk2720ur66o/rumale_header_400.png)
4
4
 
5
- [![Build Status](https://travis-ci.org/yoshoku/rumale.svg?branch=master)](https://travis-ci.org/yoshoku/rumale)
6
- [![Coverage Status](https://coveralls.io/repos/github/yoshoku/rumale/badge.svg?branch=master)](https://coveralls.io/github/yoshoku/rumale?branch=master)
5
+ [![Build Status](https://github.com/yoshoku/rumale/workflows/build/badge.svg)](https://github.com/yoshoku/rumale/actions?query=workflow%3Abuild)
7
6
  [![Gem Version](https://badge.fury.io/rb/rumale.svg)](https://badge.fury.io/rb/rumale)
8
7
  [![BSD 2-Clause License](https://img.shields.io/badge/License-BSD%202--Clause-orange.svg)](https://github.com/yoshoku/rumale/blob/master/LICENSE.txt)
9
8
  [![Documentation](https://img.shields.io/badge/api-reference-blue.svg)](https://yoshoku.github.io/rumale/doc/)
@@ -1,21 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rumale/linear_model/base_sgd'
3
+ require 'lbfgsb'
4
4
  require 'rumale/base/classifier'
5
+ require 'rumale/linear_model/base_sgd'
6
+ require 'rumale/preprocessing/label_binarizer'
5
7
 
6
8
  module Rumale
7
9
  module LinearModel
8
- # LogisticRegression is a class that implements Logistic Regression
9
- # with stochastic gradient descent optimization.
10
- # For multiclass classification problem, it uses one-vs-the-rest strategy.
10
+ # LogisticRegression is a class that implements Logistic Regression.
11
+ # In multiclass classification problem, it uses one-vs-the-rest strategy for the sgd solver
12
+ # and multinomial logistic regression for the lbfgs solver.
11
13
  #
12
- # Rumale::SVM provides Logistic Regression based on LIBLINEAR.
13
- # If you prefer execution speed, you should use Rumale::SVM::LogisticRegression.
14
- # https://github.com/yoshoku/rumale-svm
14
+ # @note
15
+ # Rumale::SVM provides Logistic Regression based on LIBLINEAR.
16
+ # If you prefer execution speed, you should use Rumale::SVM::LogisticRegression.
17
+ # https://github.com/yoshoku/rumale-svm
15
18
  #
16
19
  # @example
17
20
  # estimator =
18
- # Rumale::LinearModel::LogisticRegression.new(reg_param: 1.0, max_iter: 1000, batch_size: 50, random_seed: 1)
21
+ # Rumale::LinearModel::LogisticRegression.new(reg_param: 1.0, random_seed: 1)
19
22
  # estimator.fit(training_samples, traininig_labels)
20
23
  # results = estimator.predict(testing_samples)
21
24
  #
@@ -42,19 +45,24 @@ module Rumale
42
45
  # @return [Random]
43
46
  attr_reader :rng
44
47
 
45
- # Create a new classifier with Logisitc Regression by the SGD optimization.
48
+ # Create a new classifier with Logisitc Regression.
46
49
  #
47
50
  # @param learning_rate [Float] The initial value of learning rate.
48
51
  # The learning rate decreases as the iteration proceeds according to the equation: learning_rate / (1 + decay * t).
52
+ # If solver = 'lbfgs', this parameter is ignored.
49
53
  # @param decay [Float] The smoothing parameter for decreasing learning rate as the iteration proceeds.
50
54
  # If nil is given, the decay sets to 'reg_param * learning_rate'.
55
+ # If solver = 'lbfgs', this parameter is ignored.
51
56
  # @param momentum [Float] The momentum factor.
57
+ # If solver = 'lbfgs', this parameter is ignored.
52
58
  # @param penalty [String] The regularization type to be used ('l1', 'l2', and 'elasticnet').
59
+ # If solver = 'lbfgs', only 'l2' can be selected for this parameter.
53
60
  # @param l1_ratio [Float] The elastic-net type regularization mixing parameter.
54
61
  # If penalty set to 'l2' or 'l1', this parameter is ignored.
55
62
  # If l1_ratio = 1, the regularization is similar to Lasso.
56
63
  # If l1_ratio = 0, the regularization is similar to Ridge.
57
64
  # If 0 < l1_ratio < 1, the regularization is a combination of L1 and L2.
65
+ # If solver = 'lbfgs', this parameter is ignored.
58
66
  # @param reg_param [Float] The regularization parameter.
59
67
  # @param fit_bias [Boolean] The flag indicating whether to fit the bias term.
60
68
  # @param bias_scale [Float] The scale of the bias term.
@@ -62,28 +70,38 @@ module Rumale
62
70
  # @param max_iter [Integer] The maximum number of epochs that indicates
63
71
  # how many times the whole data is given to the training process.
64
72
  # @param batch_size [Integer] The size of the mini batches.
73
+ # If solver = 'lbfgs', this parameter is ignored.
65
74
  # @param tol [Float] The tolerance of loss for terminating optimization.
75
+ # If solver = 'lbfgs', this value is given as tol / Lbfgsb::DBL_EPSILON to the factr argument of Lbfgsb.minimize method.
76
+ # @param solver [String] The algorithm for optimization. ('lbfgs' or 'sgd').
77
+ # 'lbfgs' uses the L-BFGS with lbfgs.rb gem.
78
+ # 'sgd' uses the stochastic gradient descent optimization.
66
79
  # @param n_jobs [Integer] The number of jobs for running the fit and predict methods in parallel.
67
80
  # If nil is given, the methods do not execute in parallel.
68
81
  # If zero or less is given, it becomes equal to the number of processors.
69
- # This parameter is ignored if the Parallel gem is not loaded.
82
+ # This parameter is ignored if the Parallel gem is not loaded or the solver is 'lbfgs'.
70
83
  # @param verbose [Boolean] The flag indicating whether to output loss during iteration.
84
+ # If solver = 'lbfgs' and true is given, 'iterate.dat' file is generated by lbfgsb.rb.
71
85
  # @param random_seed [Integer] The seed value using to initialize the random generator.
72
86
  def initialize(learning_rate: 0.01, decay: nil, momentum: 0.9,
73
87
  penalty: 'l2', reg_param: 1.0, l1_ratio: 0.5,
74
88
  fit_bias: true, bias_scale: 1.0,
75
89
  max_iter: 1000, batch_size: 50, tol: 1e-4,
90
+ solver: 'lbfgs',
76
91
  n_jobs: nil, verbose: false, random_seed: nil)
77
92
  check_params_numeric(learning_rate: learning_rate, momentum: momentum,
78
93
  reg_param: reg_param, l1_ratio: l1_ratio, bias_scale: bias_scale,
79
94
  max_iter: max_iter, batch_size: batch_size, tol: tol)
80
95
  check_params_boolean(fit_bias: fit_bias, verbose: verbose)
81
- check_params_string(penalty: penalty)
96
+ check_params_string(solver: solver, penalty: penalty)
82
97
  check_params_numeric_or_nil(decay: decay, n_jobs: n_jobs, random_seed: random_seed)
83
98
  check_params_positive(learning_rate: learning_rate, reg_param: reg_param,
84
99
  bias_scale: bias_scale, max_iter: max_iter, batch_size: batch_size)
100
+ raise ArgumentError, "The 'lbfgs' solver supports only 'l2' penalties." if solver == 'lbfgs' && penalty != 'l2'
101
+
85
102
  super()
86
103
  @params.merge!(method(:initialize).parameters.map { |_t, arg| [arg, binding.local_variable_get(arg)] }.to_h)
104
+ @params[:solver] = solver == 'sgd' ? 'sgd' : 'lbfgs'
87
105
  @params[:decay] ||= @params[:reg_param] * @params[:learning_rate]
88
106
  @params[:random_seed] ||= srand
89
107
  @rng = Random.new(@params[:random_seed])
@@ -105,30 +123,10 @@ module Rumale
105
123
  check_sample_label_size(x, y)
106
124
 
107
125
  @classes = Numo::Int32[*y.to_a.uniq.sort]
108
-
109
- if multiclass_problem?
110
- n_classes = @classes.size
111
- n_features = x.shape[1]
112
- @weight_vec = Numo::DFloat.zeros(n_classes, n_features)
113
- @bias_term = Numo::DFloat.zeros(n_classes)
114
- if enable_parallel?
115
- # :nocov:
116
- models = parallel_map(n_classes) do |n|
117
- bin_y = Numo::Int32.cast(y.eq(@classes[n])) * 2 - 1
118
- partial_fit(x, bin_y)
119
- end
120
- # :nocov:
121
- n_classes.times { |n| @weight_vec[n, true], @bias_term[n] = models[n] }
122
- else
123
- n_classes.times do |n|
124
- bin_y = Numo::Int32.cast(y.eq(@classes[n])) * 2 - 1
125
- @weight_vec[n, true], @bias_term[n] = partial_fit(x, bin_y)
126
- end
127
- end
126
+ if @params[:solver] == 'sgd'
127
+ fit_sgd(x, y)
128
128
  else
129
- negative_label = @classes[0]
130
- bin_y = Numo::Int32.cast(y.ne(negative_label)) * 2 - 1
131
- @weight_vec, @bias_term = partial_fit(x, bin_y)
129
+ fit_lbfgs(x, y)
132
130
  end
133
131
 
134
132
  self
@@ -182,6 +180,96 @@ module Rumale
182
180
  def multiclass_problem?
183
181
  @classes.size > 2
184
182
  end
183
+
184
+ def fit_lbfgs(base_x, base_y)
185
+ if multiclass_problem?
186
+ fnc = proc do |w, x, y, a|
187
+ n_features = x.shape[1]
188
+ n_classes = y.shape[1]
189
+ z = x.dot(w.reshape(n_classes, n_features).transpose)
190
+ # logsumexp and softmax
191
+ z_max = z.max(-1).expand_dims(-1).dup
192
+ z_max[~z_max.isfinite] = 0.0
193
+ lgsexp = Numo::NMath.log(Numo::NMath.exp(z - z_max).sum(-1)).expand_dims(-1) + z_max
194
+ t = z - lgsexp
195
+ sftmax = Numo::NMath.exp(t)
196
+ # loss and gradient
197
+ loss = -(y * t).sum + 0.5 * a * w.dot(w)
198
+ grad = (sftmax - y).transpose.dot(x).flatten.dup + a * w
199
+ [loss, grad]
200
+ end
201
+
202
+ base_x = expand_feature(base_x) if fit_bias?
203
+ encoder = Rumale::Preprocessing::LabelBinarizer.new
204
+ onehot_y = encoder.fit_transform(base_y)
205
+ n_classes = @classes.size
206
+ n_features = base_x.shape[1]
207
+ w_init = Numo::DFloat.zeros(n_classes * n_features)
208
+
209
+ verbose = @params[:verbose] ? 1 : -1
210
+ res = Lbfgsb.minimize(
211
+ fnc: fnc, jcb: true, x_init: w_init, args: [base_x, onehot_y, @params[:reg_param]],
212
+ maxiter: @params[:max_iter], factr: @params[:tol] / Lbfgsb::DBL_EPSILON, verbose: verbose
213
+ )
214
+
215
+ if fit_bias?
216
+ weight = res[:x].reshape(n_classes, n_features)
217
+ @weight_vec = weight[true, 0...-1].dup
218
+ @bias_term = weight[true, -1].dup
219
+ else
220
+ @weight_vec = res[:x].reshape(n_classes, n_features)
221
+ @bias_term = Numo::DFloat.zeros(n_classes)
222
+ end
223
+ else
224
+ fnc = proc do |w, x, y, a|
225
+ z = 1 + Numo::NMath.exp(-y * x.dot(w))
226
+ loss = Numo::NMath.log(z).sum + 0.5 * a * w.dot(w)
227
+ grad = (y / z - y).dot(x) + a * w
228
+ [loss, grad]
229
+ end
230
+
231
+ base_x = expand_feature(base_x) if fit_bias?
232
+ negative_label = @classes[0]
233
+ bin_y = Numo::Int32.cast(base_y.ne(negative_label)) * 2 - 1
234
+ n_features = base_x.shape[1]
235
+ w_init = Numo::DFloat.zeros(n_features)
236
+
237
+ verbose = @params[:verbose] ? 1 : -1
238
+ res = Lbfgsb.minimize(
239
+ fnc: fnc, jcb: true, x_init: w_init, args: [base_x, bin_y, @params[:reg_param]],
240
+ maxiter: @params[:max_iter], factr: @params[:tol] / Lbfgsb::DBL_EPSILON, verbose: verbose
241
+ )
242
+
243
+ @weight_vec, @bias_term = split_weight(res[:x])
244
+ end
245
+ end
246
+
247
+ def fit_sgd(x, y)
248
+ if multiclass_problem?
249
+ n_classes = @classes.size
250
+ n_features = x.shape[1]
251
+ @weight_vec = Numo::DFloat.zeros(n_classes, n_features)
252
+ @bias_term = Numo::DFloat.zeros(n_classes)
253
+ if enable_parallel?
254
+ # :nocov:
255
+ models = parallel_map(n_classes) do |n|
256
+ bin_y = Numo::Int32.cast(y.eq(@classes[n])) * 2 - 1
257
+ partial_fit(x, bin_y)
258
+ end
259
+ # :nocov:
260
+ n_classes.times { |n| @weight_vec[n, true], @bias_term[n] = models[n] }
261
+ else
262
+ n_classes.times do |n|
263
+ bin_y = Numo::Int32.cast(y.eq(@classes[n])) * 2 - 1
264
+ @weight_vec[n, true], @bias_term[n] = partial_fit(x, bin_y)
265
+ end
266
+ end
267
+ else
268
+ negative_label = @classes[0]
269
+ bin_y = Numo::Int32.cast(y.ne(negative_label)) * 2 - 1
270
+ @weight_vec, @bias_term = partial_fit(x, bin_y)
271
+ end
272
+ end
185
273
  end
186
274
  end
187
275
  end
@@ -2,13 +2,13 @@
2
2
 
3
3
  require 'rumale/base/base_estimator'
4
4
  require 'rumale/base/transformer'
5
+ require 'lbfgsb'
5
6
 
6
7
  module Rumale
7
8
  module MetricLearning
8
9
  # NeighbourhoodComponentAnalysis is a class that implements Neighbourhood Component Analysis.
9
10
  #
10
11
  # @example
11
- # require 'mopti'
12
12
  # require 'rumale'
13
13
  #
14
14
  # transformer = Rumale::MetricLearning::NeighbourhoodComponentAnalysis.new
@@ -39,7 +39,9 @@ module Rumale
39
39
  # @param init [String] The initialization method for components ('random' or 'pca').
40
40
  # @param max_iter [Integer] The maximum number of iterations.
41
41
  # @param tol [Float] The tolerance of termination criterion.
42
+ # This value is given as tol / Lbfgsb::DBL_EPSILON to the factr argument of Lbfgsb.minimize method.
42
43
  # @param verbose [Boolean] The flag indicating whether to output loss during iteration.
44
+ # If true is given, 'iterate.dat' file is generated by lbfgsb.rb.
43
45
  # @param random_seed [Integer] The seed value using to initialize the random generator.
44
46
  def initialize(n_components: nil, init: 'random', max_iter: 100, tol: 1e-6, verbose: false, random_seed: nil)
45
47
  check_params_numeric_or_nil(n_components: n_components, random_seed: random_seed)
@@ -65,8 +67,6 @@ module Rumale
65
67
  # @param y [Numo::Int32] (shape: [n_samples]) The labels to be used for fitting the model.
66
68
  # @return [NeighbourhoodComponentAnalysis] The learned classifier itself.
67
69
  def fit(x, y)
68
- raise 'NeighbourhoodComponentAnalysis#fit requires Mopti but that is not loaded.' unless enable_mopti?
69
-
70
70
  x = check_convert_sample_array(x)
71
71
  y = check_convert_label_array(y)
72
72
  check_sample_label_size(x, y)
@@ -102,14 +102,6 @@ module Rumale
102
102
 
103
103
  private
104
104
 
105
- def enable_mopti?
106
- if defined?(Mopti).nil?
107
- warn('NeighbourhoodComponentAnalysis#fit requires Mopti but that is not loaded. You should intall and load mopti gem in advance.')
108
- return false
109
- end
110
- true
111
- end
112
-
113
105
  def init_components(x, n_features, n_components)
114
106
  if @params[:init] == 'pca'
115
107
  pca = Rumale::Decomposition::PCA.new(n_components: n_components)
@@ -127,28 +119,18 @@ module Rumale
127
119
  res[:x] = comp_init
128
120
  res[:n_iter] = 0
129
121
  # perform optimization.
130
- optimizer = Mopti::ScaledConjugateGradient.new(
131
- fnc: method(:nca_loss), jcb: method(:nca_dloss),
132
- x_init: comp_init, args: [x, y],
133
- max_iter: @params[:max_iter], ftol: @params[:tol]
122
+ verbose = @params[:verbose] ? 1 : -1
123
+ res = Lbfgsb.minimize(
124
+ fnc: method(:nca_fnc), jcb: true, x_init: comp_init, args: [x, y],
125
+ maxiter: @params[:max_iter], factr: @params[:tol] / Lbfgsb::DBL_EPSILON, verbose: verbose
134
126
  )
135
- fold = 0.0
136
- dold = 0.0
137
- optimizer.each do |prm|
138
- res = prm
139
- puts "[NeighbourhoodComponentAnalysis] The value of objective function after #{res[:n_iter]} epochs: #{x.shape[0] - res[:fnc]}" if @params[:verbose]
140
- break if (fold - res[:fnc]).abs <= @params[:tol] && (dold - res[:jcb]).abs <= @params[:tol]
141
-
142
- fold = res[:fnc]
143
- dold = res[:jcb]
144
- end
145
127
  # return the results.
146
128
  n_iter = res[:n_iter]
147
129
  comps = n_components == 1 ? res[:x].dup : res[:x].reshape(n_components, n_features)
148
130
  [comps, n_iter]
149
131
  end
150
132
 
151
- def nca_loss(w, x, y)
133
+ def nca_fnc(w, x, y)
152
134
  # initialize some variables.
153
135
  n_samples, n_features = x.shape
154
136
  n_components = w.size / n_features
@@ -157,32 +139,18 @@ module Rumale
157
139
  z = x.dot(w.transpose)
158
140
  # calculate probability matrix.
159
141
  prob_mat = probability_matrix(z)
160
- # calculate loss.
142
+ # calculate loss and gradient.
161
143
  # NOTE:
162
144
  # NCA attempts to maximize its objective function.
163
145
  # For the minization algorithm, the objective function value is subtracted from the maixmum value (n_samples).
164
146
  mask_mat = y.expand_dims(1).eq(y)
165
147
  masked_prob_mat = prob_mat * mask_mat
166
- n_samples - masked_prob_mat.sum
167
- end
168
-
169
- def nca_dloss(w, x, y)
170
- # initialize some variables.
171
- n_features = x.shape[1]
172
- n_components = w.size / n_features
173
- # projection.
174
- w = w.reshape(n_components, n_features)
175
- z = x.dot(w.transpose)
176
- # calculate probability matrix.
177
- prob_mat = probability_matrix(z)
178
- # calculate gradient.
179
- mask_mat = y.expand_dims(1).eq(y)
180
- masked_prob_mat = prob_mat * mask_mat
148
+ loss = n_samples - masked_prob_mat.sum
181
149
  weighted_prob_mat = masked_prob_mat - prob_mat * masked_prob_mat.sum(1).expand_dims(1)
182
150
  weighted_prob_mat += weighted_prob_mat.transpose
183
151
  weighted_prob_mat[weighted_prob_mat.diag_indices] = -weighted_prob_mat.sum(0)
184
- gradient = 2 * z.transpose.dot(weighted_prob_mat).dot(x)
185
- -gradient.flatten.dup
152
+ gradient = -2 * z.transpose.dot(weighted_prob_mat).dot(x)
153
+ [loss, gradient.flatten.dup]
186
154
  end
187
155
 
188
156
  def probability_matrix(z)
@@ -3,5 +3,5 @@
3
3
  # Rumale is a machine learning library in Ruby.
4
4
  module Rumale
5
5
  # The version of Rumale you are using.
6
- VERSION = '0.21.0'
6
+ VERSION = '0.22.0'
7
7
  end
@@ -45,4 +45,5 @@ Gem::Specification.new do |spec|
45
45
  }
46
46
 
47
47
  spec.add_runtime_dependency 'numo-narray', '>= 0.9.1'
48
+ spec.add_runtime_dependency 'lbfgsb', '>=0.3.0'
48
49
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rumale
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.21.0
4
+ version: 0.22.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - yoshoku
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-10-02 00:00:00.000000000 Z
11
+ date: 2020-11-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: numo-narray
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.9.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: lbfgsb
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.3.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 0.3.0
27
41
  description: |
28
42
  Rumale is a machine learning library in Ruby.
29
43
  Rumale provides machine learning algorithms with interfaces similar to Scikit-Learn in Python.
@@ -43,7 +57,7 @@ extensions:
43
57
  - ext/rumale/extconf.rb
44
58
  extra_rdoc_files: []
45
59
  files:
46
- - ".coveralls.yml"
60
+ - ".github/workflows/build.yml"
47
61
  - ".gitignore"
48
62
  - ".rspec"
49
63
  - ".rubocop.yml"
@@ -209,7 +223,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
209
223
  - !ruby/object:Gem::Version
210
224
  version: '0'
211
225
  requirements: []
212
- rubygems_version: 3.1.2
226
+ rubygems_version: 3.1.4
213
227
  signing_key:
214
228
  specification_version: 4
215
229
  summary: Rumale is a machine learning library in Ruby. Rumale provides machine learning
@@ -1 +0,0 @@
1
- service_name: travis-ci