rumale 0.17.0 → 0.17.1

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: 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