rumale 0.17.0 → 0.17.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d1071dfdccfc177ea5902e5e1b09fce084fd4b6ce403fae6797e6b93c3f826ad
4
- data.tar.gz: 30768881f5c826f59dbcca0b17a1192dbdc17ca835c8bcc626e874391131bf92
3
+ metadata.gz: 54719fad09a9036dbdc1430323272af7f3c3e746aecac9b6199eef70bfe07856
4
+ data.tar.gz: b9df7b5ff0cc4feebb053955aea4d022e72263be1288578906b67f2100b97160
5
5
  SHA512:
6
- metadata.gz: e748eedf78b040a7dbe1a1b744f87f1c1e9c3ae751417711eccd5f69dca68335f1a206b9258503da70a8486c3d8588d61ae78081ebdecc6a8ee40f85383a319f
7
- data.tar.gz: 2abae603660179e05f8341ab5351fb9e028549674bb13901e4cae4dfd13c99995de0de3c63f8c75182acd155a1d2171b02e4db74fcaa08c1108a1a0e92ad3eee
6
+ metadata.gz: 42aedd744761fb61e6fcfa32643ee56b267ea163a2376f3eb308b181cd5981ceef4270e88ed1fe322e5f198d94b3b34af277f246e99f0439cfc3699c3838d76e
7
+ data.tar.gz: '082dda62d97b2655413ef599185b38c88cceacdec3ceecb99aab67b222306b5478963dfa7e216daa56838c6c3a9ac65ced53e51d513ae23ddf0954fb2c5982e5'
@@ -1,3 +1,9 @@
1
+ # 0.17.1
2
+ - Add transformer class for [PolynomialFeatures](https://yoshoku.github.io/rumale/doc/Rumale/Preprocessing/PolynomialFeatures.html)
3
+ - Add verbose and tol parameter to [FactorizationMachineClassifier](https://yoshoku.github.io/rumale/doc/Rumale/PolynomialModel/FactorizationMachineClassifier.html)
4
+ and [FactorizationMachineRegressor](https://yoshoku.github.io/rumale/doc/Rumale/PolynomialModel/FactorizationMachineRegressor.html)
5
+ - Fix bug that factor elements of Factorization Machines estimators are not learned caused by initializing factors to zero.
6
+
1
7
  # 0.17.0
2
8
  ## Breaking changes
3
9
  - Fix all linear model estimators to use the new abstract class ([BaseSGD](https://yoshoku.github.io/rumale/doc/Rumale/LinearModel/BaseSGD.html)) introduced in version 0.16.1.
@@ -91,6 +91,7 @@ require 'rumale/preprocessing/label_binarizer'
91
91
  require 'rumale/preprocessing/label_encoder'
92
92
  require 'rumale/preprocessing/one_hot_encoder'
93
93
  require 'rumale/preprocessing/ordinal_encoder'
94
+ require 'rumale/preprocessing/polynomial_features'
94
95
  require 'rumale/model_selection/k_fold'
95
96
  require 'rumale/model_selection/stratified_k_fold'
96
97
  require 'rumale/model_selection/shuffle_split'
@@ -7,11 +7,6 @@ module Rumale
7
7
  module Optimizer
8
8
  # AdaGrad is a class that implements AdaGrad optimizer.
9
9
  #
10
- # @example
11
- # optimizer = Rumale::Optimizer::AdaGrad.new(learning_rate: 0.01, momentum: 0.9)
12
- # estimator = Rumale::LinearModel::LinearRegression.new(optimizer: optimizer, random_seed: 1)
13
- # estimator.fit(samples, values)
14
- #
15
10
  # *Reference*
16
11
  # - J. Duchi, E Hazan, and Y. Singer, "Adaptive Subgradient Methods for Online Learning and Stochastic Optimization," J. Machine Learning Research, vol. 12, pp. 2121--2159, 2011.
17
12
  class AdaGrad
@@ -7,11 +7,6 @@ module Rumale
7
7
  module Optimizer
8
8
  # Adam is a class that implements Adam optimizer.
9
9
  #
10
- # @example
11
- # optimizer = Rumale::Optimizer::Adam.new(learning_rate: 0.01, momentum: 0.9, decay1: 0.9, decay2: 0.999)
12
- # estimator = Rumale::LinearModel::LinearRegression.new(optimizer: optimizer, random_seed: 1)
13
- # estimator.fit(samples, values)
14
- #
15
10
  # *Reference*
16
11
  # - D P. Kingma and J. Ba, "Adam: A Method for Stochastic Optimization," Proc. ICLR'15, 2015.
17
12
  class Adam
@@ -8,11 +8,6 @@ module Rumale
8
8
  module Optimizer
9
9
  # Nadam is a class that implements Nadam optimizer.
10
10
  #
11
- # @example
12
- # optimizer = Rumale::Optimizer::Nadam.new(learning_rate: 0.01, decay1: 0.9, decay2: 0.999)
13
- # estimator = Rumale::LinearModel::LinearRegression.new(optimizer: optimizer, random_seed: 1)
14
- # estimator.fit(samples, values)
15
- #
16
11
  # *Reference*
17
12
  # - T. Dozat, "Incorporating Nesterov Momentum into Adam," Tech. Repo. Stanford University, 2015.
18
13
  class Nadam
@@ -7,11 +7,6 @@ module Rumale
7
7
  module Optimizer
8
8
  # RMSProp is a class that implements RMSProp optimizer.
9
9
  #
10
- # @example
11
- # optimizer = Rumale::Optimizer::RMSProp.new(learning_rate: 0.01, momentum: 0.9, decay: 0.9)
12
- # estimator = Rumale::LinearModel::LinearRegression.new(optimizer: optimizer, random_seed: 1)
13
- # estimator.fit(samples, values)
14
- #
15
10
  # *Reference*
16
11
  # - I. Sutskever, J. Martens, G. Dahl, and G. Hinton, "On the importance of initialization and momentum in deep learning," Proc. ICML' 13, pp. 1139--1147, 2013.
17
12
  # - G. Hinton, N. Srivastava, and K. Swersky, "Lecture 6e rmsprop," Neural Networks for Machine Learning, 2012.
@@ -6,11 +6,6 @@ require 'rumale/base/base_estimator'
6
6
  module Rumale
7
7
  module Optimizer
8
8
  # SGD is a class that implements SGD optimizer.
9
- #
10
- # @example
11
- # optimizer = Rumale::Optimizer::SGD.new(learning_rate: 0.01, momentum: 0.9, decay: 0.9)
12
- # estimator = Rumale::LinearModel::LinearRegression.new(optimizer: optimizer, random_seed: 1)
13
- # estimator.fit(samples, values)
14
9
  class SGD
15
10
  include Base::BaseEstimator
16
11
  include Validation
@@ -7,11 +7,6 @@ module Rumale
7
7
  module Optimizer
8
8
  # YellowFin is a class that implements YellowFin optimizer.
9
9
  #
10
- # @example
11
- # optimizer = Rumale::Optimizer::YellowFin.new(learning_rate: 0.01, momentum: 0.9, decay: 0.999, window_width: 20)
12
- # estimator = Rumale::LinearModel::LinearRegression.new(optimizer: optimizer, random_seed: 1)
13
- # estimator.fit(samples, values)
14
- #
15
10
  # *Reference*
16
11
  # - J. Zhang and I. Mitliagkas, "YellowFin and the Art of Momentum Tuning," CoRR abs/1706.03471, 2017.
17
12
  class YellowFin
@@ -20,15 +20,18 @@ module Rumale
20
20
  # @param max_iter [Integer] The maximum number of epochs that indicates
21
21
  # how many times the whole data is given to the training process.
22
22
  # @param batch_size [Integer] The size of the mini batches.
23
+ # @param tol [Float] The tolerance of loss for terminating optimization.
23
24
  # @param optimizer [Optimizer] The optimizer to calculate adaptive learning rate.
24
25
  # If nil is given, Nadam is used.
25
26
  # @param n_jobs [Integer] The number of jobs for running the fit and predict methods in parallel.
26
27
  # If nil is given, the methods do not execute in parallel.
27
28
  # If zero or less is given, it becomes equal to the number of processors.
28
29
  # This parameter is ignored if the Parallel gem is not loaded.
30
+ # @param verbose [Boolean] The flag indicating whether to output loss during iteration.
29
31
  # @param random_seed [Integer] The seed value using to initialize the random generator.
30
32
  def initialize(n_factors: 2, loss: nil, reg_param_linear: 1.0, reg_param_factor: 1.0,
31
- max_iter: 200, batch_size: 50, optimizer: nil, n_jobs: nil, random_seed: nil)
33
+ max_iter: 200, batch_size: 50, tol: 1e-4,
34
+ optimizer: nil, n_jobs: nil, verbose: false, random_seed: nil)
32
35
  @params = {}
33
36
  @params[:n_factors] = n_factors
34
37
  @params[:loss] = loss unless loss.nil?
@@ -36,9 +39,11 @@ module Rumale
36
39
  @params[:reg_param_factor] = reg_param_factor
37
40
  @params[:max_iter] = max_iter
38
41
  @params[:batch_size] = batch_size
42
+ @params[:tol] = tol
39
43
  @params[:optimizer] = optimizer
40
44
  @params[:optimizer] ||= Optimizer::Nadam.new
41
45
  @params[:n_jobs] = n_jobs
46
+ @params[:verbose] = verbose
42
47
  @params[:random_seed] = random_seed
43
48
  @params[:random_seed] ||= srand
44
49
  @factor_mat = nil
@@ -51,14 +56,15 @@ module Rumale
51
56
 
52
57
  def partial_fit(x, y)
53
58
  # Initialize some variables.
59
+ class_name = self.class.to_s.split('::').last if @params[:verbose]
54
60
  n_samples, n_features = x.shape
55
61
  sub_rng = @rng.dup
56
62
  weight_vec = Numo::DFloat.zeros(n_features + 1)
57
- factor_mat = Numo::DFloat.zeros(@params[:n_factors], n_features)
63
+ factor_mat = Rumale::Utils.rand_normal([@params[:n_factors], n_features], sub_rng)
58
64
  weight_optimizer = @params[:optimizer].dup
59
65
  factor_optimizers = Array.new(@params[:n_factors]) { @params[:optimizer].dup }
60
66
  # Start optimization.
61
- @params[:max_iter].times do |_t|
67
+ @params[:max_iter].times do |t|
62
68
  sample_ids = [*0...n_samples]
63
69
  sample_ids.shuffle!(random: sub_rng)
64
70
  until (subset_ids = sample_ids.shift(@params[:batch_size])).empty?
@@ -76,10 +82,17 @@ module Rumale
76
82
  factor_gradient(loss_grad, sub_x, factor_mat[n, true]))
77
83
  end
78
84
  end
85
+ loss = loss_func(x, expand_feature(x), y, factor_mat, weight_vec)
86
+ puts "[#{class_name}] Loss after #{t + 1} epochs: #{loss}" if @params[:verbose]
87
+ break if loss < @params[:tol]
79
88
  end
80
89
  [factor_mat, *split_weight_vec_bias(weight_vec)]
81
90
  end
82
91
 
92
+ def loss_func(_x, _expanded_x, _y, _factor, _weight)
93
+ raise NotImplementedError, "#{__method__} has to be implemented in #{self.class}."
94
+ end
95
+
83
96
  def loss_gradient(_x, _expanded_x, _y, _factor, _weight)
84
97
  raise NotImplementedError, "#{__method__} has to be implemented in #{self.class}."
85
98
  end
@@ -53,18 +53,22 @@ module Rumale
53
53
  # @param max_iter [Integer] The maximum number of epochs that indicates
54
54
  # how many times the whole data is given to the training process.
55
55
  # @param batch_size [Integer] The size of the mini batches.
56
+ # @param tol [Float] The tolerance of loss for terminating optimization.
56
57
  # @param optimizer [Optimizer] The optimizer to calculate adaptive learning rate.
57
58
  # If nil is given, Nadam is used.
58
59
  # @param n_jobs [Integer] The number of jobs for running the fit and predict methods in parallel.
59
60
  # If nil is given, the methods do not execute in parallel.
60
61
  # If zero or less is given, it becomes equal to the number of processors.
61
62
  # This parameter is ignored if the Parallel gem is not loaded.
63
+ # @param verbose [Boolean] The flag indicating whether to output loss during iteration.
62
64
  # @param random_seed [Integer] The seed value using to initialize the random generator.
63
65
  def initialize(n_factors: 2, loss: 'hinge', reg_param_linear: 1.0, reg_param_factor: 1.0,
64
- max_iter: 200, batch_size: 50, optimizer: nil, n_jobs: nil, random_seed: nil)
66
+ max_iter: 200, batch_size: 50, tol: 1e-4,
67
+ optimizer: nil, n_jobs: nil, verbose: false, random_seed: nil)
65
68
  check_params_numeric(reg_param_linear: reg_param_linear, reg_param_factor: reg_param_factor,
66
- n_factors: n_factors, max_iter: max_iter, batch_size: batch_size)
69
+ n_factors: n_factors, max_iter: max_iter, batch_size: batch_size, tol: tol)
67
70
  check_params_string(loss: loss)
71
+ check_params_boolean(verbose: verbose)
68
72
  check_params_numeric_or_nil(n_jobs: n_jobs, random_seed: random_seed)
69
73
  check_params_positive(n_factors: n_factors,
70
74
  reg_param_linear: reg_param_linear, reg_param_factor: reg_param_factor,
@@ -196,6 +200,15 @@ module Rumale
196
200
  ex_x.dot(weight) + 0.5 * (factor.dot(x.transpose)**2 - (factor**2).dot(x.transpose**2)).sum(0)
197
201
  end
198
202
 
203
+ def loss_func(x, ex_x, y, factor, weight)
204
+ z = bin_decision_function(x, ex_x, factor, weight)
205
+ if @params[:loss] == 'hinge'
206
+ z.class.maximum(0.0, 1 - y * z).sum.fdiv(y.shape[0])
207
+ else
208
+ Numo::NMath.log(1 + Numo::NMath.exp(-y * z)).sum.fdiv(y.shape[0])
209
+ end
210
+ end
211
+
199
212
  def hinge_loss_gradient(x, ex_x, y, factor, weight)
200
213
  evaluated = y * bin_decision_function(x, ex_x, factor, weight)
201
214
  gradient = Numo::DFloat.zeros(evaluated.size)
@@ -46,17 +46,21 @@ module Rumale
46
46
  # @param max_iter [Integer] The maximum number of epochs that indicates
47
47
  # how many times the whole data is given to the training process.
48
48
  # @param batch_size [Integer] The size of the mini batches.
49
+ # @param tol [Float] The tolerance of loss for terminating optimization.
49
50
  # @param optimizer [Optimizer] The optimizer to calculate adaptive learning rate.
50
51
  # If nil is given, Nadam is used.
51
52
  # @param n_jobs [Integer] The number of jobs for running the fit method in parallel.
52
53
  # If nil is given, the method does not execute in parallel.
53
54
  # If zero or less is given, it becomes equal to the number of processors.
54
55
  # This parameter is ignored if the Parallel gem is not loaded.
56
+ # @param verbose [Boolean] The flag indicating whether to output loss during iteration.
55
57
  # @param random_seed [Integer] The seed value using to initialize the random generator.
56
58
  def initialize(n_factors: 2, reg_param_linear: 1.0, reg_param_factor: 1.0,
57
- max_iter: 200, batch_size: 50, optimizer: nil, n_jobs: nil, random_seed: nil)
59
+ max_iter: 200, batch_size: 50, tol: 1e-4,
60
+ optimizer: nil, n_jobs: nil, verbose: false, random_seed: nil)
58
61
  check_params_numeric(reg_param_linear: reg_param_linear, reg_param_factor: reg_param_factor,
59
- n_factors: n_factors, max_iter: max_iter, batch_size: batch_size)
62
+ n_factors: n_factors, max_iter: max_iter, batch_size: batch_size, tol: tol)
63
+ check_params_boolean(verbose: verbose)
60
64
  check_params_numeric_or_nil(n_jobs: n_jobs, random_seed: random_seed)
61
65
  check_params_positive(n_factors: n_factors, reg_param_linear: reg_param_linear, reg_param_factor: reg_param_factor,
62
66
  max_iter: max_iter, batch_size: batch_size)
@@ -132,6 +136,11 @@ module Rumale
132
136
 
133
137
  private
134
138
 
139
+ def loss_func(x, ex_x, y, factor, weight)
140
+ z = ex_x.dot(weight) + 0.5 * (factor.dot(x.transpose)**2 - (factor**2).dot(x.transpose**2)).sum(0)
141
+ ((z - y)**2).sum.fdiv(y.shape[0])
142
+ end
143
+
135
144
  def loss_gradient(x, ex_x, y, factor, weight)
136
145
  z = ex_x.dot(weight) + 0.5 * (factor.dot(x.transpose)**2 - (factor**2).dot(x.transpose**2)).sum(0)
137
146
  2.0 * (z - y)
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rumale/base/base_estimator'
4
+ require 'rumale/base/transformer'
5
+
6
+ module Rumale
7
+ module Preprocessing
8
+ # Generating polynomial features from the given samples.
9
+ #
10
+ # @example
11
+ # require 'rumale'
12
+ #
13
+ # transformer = Rumale::Preprocessing::PolynomialFeatures.new(degree: 2)
14
+ # x = Numo::DFloat[[0, 1], [2, 3], [4, 5]]
15
+ # z = transformer.fit_transform(x)
16
+ # p z
17
+ #
18
+ # # Numo::DFloat#shape=[3,6]
19
+ # # [[1, 0, 1, 0, 0, 1],
20
+ # # [1, 2, 3, 4, 6, 9],
21
+ # # [1, 4, 5, 16, 20, 25]]
22
+ #
23
+ # # If you want to perform polynomial regression, combine it with LinearRegression as follows:
24
+ # ply = Rumale::Preprocessing::PolynomialFeatures.new(degree: 2)
25
+ # reg = Rumale::LinearModel::LinearRegression.new(fit_bias: false, random_seed: 1)
26
+ # pipeline = Rumale::Pipeline::Pipeline.new(steps: { trs: ply, est: reg })
27
+ # pipeline.fit(training_samples, training_values)
28
+ # results = pipeline.predict(testing_samples)
29
+ #
30
+ class PolynomialFeatures
31
+ include Base::BaseEstimator
32
+ include Base::Transformer
33
+
34
+ # Return the number of polynomial features.
35
+ # @return [Integer]
36
+ attr_reader :n_output_features
37
+
38
+ # Create a transformer for generating polynomial features.
39
+ #
40
+ # @param degree [Integer] The degree of polynomial features.
41
+ def initialize(degree: 2)
42
+ check_params_numeric(degree: degree)
43
+ raise ArgumentError, 'Expect the value of degree parameter greater than or eqaul to 1.' if degree < 1
44
+ @params = {}
45
+ @params[:degree] = degree
46
+ @n_output_features = nil
47
+ end
48
+
49
+ # Calculate the number of output polynomial fetures.
50
+ #
51
+ # @overload fit(x) -> PolynomialFeatures
52
+ # @param x [Numo::DFloat] (shape: [n_samples, n_features]) The samples to calculate the number of output polynomial fetures.
53
+ # @return [PolynomialFeatures]
54
+ def fit(x, _y = nil)
55
+ x = check_convert_sample_array(x)
56
+ n_features = x.shape[1]
57
+ @n_output_features = 1
58
+ @params[:degree].times do |t|
59
+ @n_output_features += Array.new(n_features) { |n| n }.repeated_combination(t + 1).size
60
+ end
61
+ self
62
+ end
63
+
64
+ # Calculate the number of polynomial features, and then transform samples to polynomial features.
65
+ #
66
+ # @overload fit_transform(x) -> Numo::DFloat
67
+ # @param x [Numo::DFloat] (shape: [n_samples, n_features]) The samples to calculate the number of polynomial features
68
+ # and be transformed.
69
+ # @return [Numo::DFloat] (shape: [n_samples, n_output_features]) The transformed samples.
70
+ def fit_transform(x, _y = nil)
71
+ x = check_convert_sample_array(x)
72
+ fit(x).transform(x)
73
+ end
74
+
75
+ # Transform the given samples to polynomial features.
76
+ #
77
+ # @param x [Numo::DFloat] (shape: [n_samples, n_features]) The samples to be transformed.
78
+ # @return [Numo::DFloat] (shape: [n_samples, n_output_features]) The transformed samples.
79
+ def transform(x)
80
+ x = check_convert_sample_array(x)
81
+ # initialize transformed features
82
+ n_samples, n_features = x.shape
83
+ z = Numo::DFloat.zeros(n_samples, n_output_features)
84
+ # bias
85
+ z[true, 0] = 1
86
+ curr_col = 1
87
+ # itself
88
+ z[true, 1..n_features] = x
89
+ curr_col += n_features
90
+ # high degree features
91
+ curr_feat_ids = Array.new(n_features + 1) { |n| n + 1 }
92
+ (1...@params[:degree]).each do
93
+ next_feat_ids = []
94
+ n_features.times do |d|
95
+ f_range = curr_feat_ids[d]...curr_feat_ids.last
96
+ next_col = curr_col + f_range.size
97
+ z[true, curr_col...next_col] = z[true, f_range] * x[true, d..d]
98
+ next_feat_ids.push(curr_col)
99
+ curr_col = next_col
100
+ end
101
+ next_feat_ids.push(curr_col)
102
+ curr_feat_ids = next_feat_ids
103
+ end
104
+ z
105
+ end
106
+ end
107
+ end
108
+ end
@@ -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.17.0'
6
+ VERSION = '0.17.1'
7
7
  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.17.0
4
+ version: 0.17.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - yoshoku
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-01-18 00:00:00.000000000 Z
11
+ date: 2020-01-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: numo-narray
@@ -258,6 +258,7 @@ files:
258
258
  - lib/rumale/preprocessing/min_max_scaler.rb
259
259
  - lib/rumale/preprocessing/one_hot_encoder.rb
260
260
  - lib/rumale/preprocessing/ordinal_encoder.rb
261
+ - lib/rumale/preprocessing/polynomial_features.rb
261
262
  - lib/rumale/preprocessing/standard_scaler.rb
262
263
  - lib/rumale/probabilistic_output.rb
263
264
  - lib/rumale/tree/base_decision_tree.rb