rumale 0.23.3 → 0.24.0

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.
Files changed (142) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +5 -1
  3. data/README.md +3 -288
  4. data/lib/rumale/version.rb +1 -1
  5. data/lib/rumale.rb +20 -131
  6. metadata +252 -150
  7. data/CHANGELOG.md +0 -643
  8. data/CODE_OF_CONDUCT.md +0 -74
  9. data/ext/rumale/extconf.rb +0 -37
  10. data/ext/rumale/rumaleext.c +0 -545
  11. data/ext/rumale/rumaleext.h +0 -12
  12. data/lib/rumale/base/base_estimator.rb +0 -49
  13. data/lib/rumale/base/classifier.rb +0 -36
  14. data/lib/rumale/base/cluster_analyzer.rb +0 -31
  15. data/lib/rumale/base/evaluator.rb +0 -17
  16. data/lib/rumale/base/regressor.rb +0 -36
  17. data/lib/rumale/base/splitter.rb +0 -21
  18. data/lib/rumale/base/transformer.rb +0 -22
  19. data/lib/rumale/clustering/dbscan.rb +0 -123
  20. data/lib/rumale/clustering/gaussian_mixture.rb +0 -218
  21. data/lib/rumale/clustering/hdbscan.rb +0 -291
  22. data/lib/rumale/clustering/k_means.rb +0 -122
  23. data/lib/rumale/clustering/k_medoids.rb +0 -141
  24. data/lib/rumale/clustering/mini_batch_k_means.rb +0 -139
  25. data/lib/rumale/clustering/power_iteration.rb +0 -127
  26. data/lib/rumale/clustering/single_linkage.rb +0 -203
  27. data/lib/rumale/clustering/snn.rb +0 -76
  28. data/lib/rumale/clustering/spectral_clustering.rb +0 -115
  29. data/lib/rumale/dataset.rb +0 -246
  30. data/lib/rumale/decomposition/factor_analysis.rb +0 -150
  31. data/lib/rumale/decomposition/fast_ica.rb +0 -188
  32. data/lib/rumale/decomposition/nmf.rb +0 -124
  33. data/lib/rumale/decomposition/pca.rb +0 -159
  34. data/lib/rumale/ensemble/ada_boost_classifier.rb +0 -179
  35. data/lib/rumale/ensemble/ada_boost_regressor.rb +0 -160
  36. data/lib/rumale/ensemble/extra_trees_classifier.rb +0 -139
  37. data/lib/rumale/ensemble/extra_trees_regressor.rb +0 -125
  38. data/lib/rumale/ensemble/gradient_boosting_classifier.rb +0 -306
  39. data/lib/rumale/ensemble/gradient_boosting_regressor.rb +0 -237
  40. data/lib/rumale/ensemble/random_forest_classifier.rb +0 -189
  41. data/lib/rumale/ensemble/random_forest_regressor.rb +0 -153
  42. data/lib/rumale/ensemble/stacking_classifier.rb +0 -215
  43. data/lib/rumale/ensemble/stacking_regressor.rb +0 -163
  44. data/lib/rumale/ensemble/voting_classifier.rb +0 -126
  45. data/lib/rumale/ensemble/voting_regressor.rb +0 -82
  46. data/lib/rumale/evaluation_measure/accuracy.rb +0 -29
  47. data/lib/rumale/evaluation_measure/adjusted_rand_score.rb +0 -74
  48. data/lib/rumale/evaluation_measure/calinski_harabasz_score.rb +0 -56
  49. data/lib/rumale/evaluation_measure/davies_bouldin_score.rb +0 -53
  50. data/lib/rumale/evaluation_measure/explained_variance_score.rb +0 -39
  51. data/lib/rumale/evaluation_measure/f_score.rb +0 -50
  52. data/lib/rumale/evaluation_measure/function.rb +0 -147
  53. data/lib/rumale/evaluation_measure/log_loss.rb +0 -45
  54. data/lib/rumale/evaluation_measure/mean_absolute_error.rb +0 -29
  55. data/lib/rumale/evaluation_measure/mean_squared_error.rb +0 -29
  56. data/lib/rumale/evaluation_measure/mean_squared_log_error.rb +0 -29
  57. data/lib/rumale/evaluation_measure/median_absolute_error.rb +0 -30
  58. data/lib/rumale/evaluation_measure/mutual_information.rb +0 -49
  59. data/lib/rumale/evaluation_measure/normalized_mutual_information.rb +0 -53
  60. data/lib/rumale/evaluation_measure/precision.rb +0 -50
  61. data/lib/rumale/evaluation_measure/precision_recall.rb +0 -96
  62. data/lib/rumale/evaluation_measure/purity.rb +0 -40
  63. data/lib/rumale/evaluation_measure/r2_score.rb +0 -43
  64. data/lib/rumale/evaluation_measure/recall.rb +0 -50
  65. data/lib/rumale/evaluation_measure/roc_auc.rb +0 -130
  66. data/lib/rumale/evaluation_measure/silhouette_score.rb +0 -82
  67. data/lib/rumale/feature_extraction/feature_hasher.rb +0 -110
  68. data/lib/rumale/feature_extraction/hash_vectorizer.rb +0 -155
  69. data/lib/rumale/feature_extraction/tfidf_transformer.rb +0 -113
  70. data/lib/rumale/kernel_approximation/nystroem.rb +0 -126
  71. data/lib/rumale/kernel_approximation/rbf.rb +0 -102
  72. data/lib/rumale/kernel_machine/kernel_fda.rb +0 -120
  73. data/lib/rumale/kernel_machine/kernel_pca.rb +0 -97
  74. data/lib/rumale/kernel_machine/kernel_ridge.rb +0 -82
  75. data/lib/rumale/kernel_machine/kernel_ridge_classifier.rb +0 -92
  76. data/lib/rumale/kernel_machine/kernel_svc.rb +0 -193
  77. data/lib/rumale/linear_model/base_sgd.rb +0 -285
  78. data/lib/rumale/linear_model/elastic_net.rb +0 -119
  79. data/lib/rumale/linear_model/lasso.rb +0 -115
  80. data/lib/rumale/linear_model/linear_regression.rb +0 -201
  81. data/lib/rumale/linear_model/logistic_regression.rb +0 -275
  82. data/lib/rumale/linear_model/nnls.rb +0 -137
  83. data/lib/rumale/linear_model/ridge.rb +0 -209
  84. data/lib/rumale/linear_model/svc.rb +0 -213
  85. data/lib/rumale/linear_model/svr.rb +0 -132
  86. data/lib/rumale/manifold/mds.rb +0 -155
  87. data/lib/rumale/manifold/tsne.rb +0 -222
  88. data/lib/rumale/metric_learning/fisher_discriminant_analysis.rb +0 -113
  89. data/lib/rumale/metric_learning/mlkr.rb +0 -161
  90. data/lib/rumale/metric_learning/neighbourhood_component_analysis.rb +0 -167
  91. data/lib/rumale/model_selection/cross_validation.rb +0 -125
  92. data/lib/rumale/model_selection/function.rb +0 -42
  93. data/lib/rumale/model_selection/grid_search_cv.rb +0 -225
  94. data/lib/rumale/model_selection/group_k_fold.rb +0 -93
  95. data/lib/rumale/model_selection/group_shuffle_split.rb +0 -115
  96. data/lib/rumale/model_selection/k_fold.rb +0 -81
  97. data/lib/rumale/model_selection/shuffle_split.rb +0 -90
  98. data/lib/rumale/model_selection/stratified_k_fold.rb +0 -99
  99. data/lib/rumale/model_selection/stratified_shuffle_split.rb +0 -118
  100. data/lib/rumale/model_selection/time_series_split.rb +0 -91
  101. data/lib/rumale/multiclass/one_vs_rest_classifier.rb +0 -83
  102. data/lib/rumale/naive_bayes/base_naive_bayes.rb +0 -47
  103. data/lib/rumale/naive_bayes/bernoulli_nb.rb +0 -82
  104. data/lib/rumale/naive_bayes/complement_nb.rb +0 -85
  105. data/lib/rumale/naive_bayes/gaussian_nb.rb +0 -69
  106. data/lib/rumale/naive_bayes/multinomial_nb.rb +0 -74
  107. data/lib/rumale/naive_bayes/negation_nb.rb +0 -71
  108. data/lib/rumale/nearest_neighbors/k_neighbors_classifier.rb +0 -133
  109. data/lib/rumale/nearest_neighbors/k_neighbors_regressor.rb +0 -108
  110. data/lib/rumale/nearest_neighbors/vp_tree.rb +0 -132
  111. data/lib/rumale/neural_network/adam.rb +0 -56
  112. data/lib/rumale/neural_network/base_mlp.rb +0 -248
  113. data/lib/rumale/neural_network/mlp_classifier.rb +0 -120
  114. data/lib/rumale/neural_network/mlp_regressor.rb +0 -90
  115. data/lib/rumale/pairwise_metric.rb +0 -152
  116. data/lib/rumale/pipeline/feature_union.rb +0 -69
  117. data/lib/rumale/pipeline/pipeline.rb +0 -175
  118. data/lib/rumale/preprocessing/bin_discretizer.rb +0 -93
  119. data/lib/rumale/preprocessing/binarizer.rb +0 -60
  120. data/lib/rumale/preprocessing/kernel_calculator.rb +0 -92
  121. data/lib/rumale/preprocessing/l1_normalizer.rb +0 -62
  122. data/lib/rumale/preprocessing/l2_normalizer.rb +0 -63
  123. data/lib/rumale/preprocessing/label_binarizer.rb +0 -89
  124. data/lib/rumale/preprocessing/label_encoder.rb +0 -79
  125. data/lib/rumale/preprocessing/max_abs_scaler.rb +0 -61
  126. data/lib/rumale/preprocessing/max_normalizer.rb +0 -62
  127. data/lib/rumale/preprocessing/min_max_scaler.rb +0 -76
  128. data/lib/rumale/preprocessing/one_hot_encoder.rb +0 -100
  129. data/lib/rumale/preprocessing/ordinal_encoder.rb +0 -109
  130. data/lib/rumale/preprocessing/polynomial_features.rb +0 -109
  131. data/lib/rumale/preprocessing/standard_scaler.rb +0 -71
  132. data/lib/rumale/probabilistic_output.rb +0 -114
  133. data/lib/rumale/tree/base_decision_tree.rb +0 -150
  134. data/lib/rumale/tree/decision_tree_classifier.rb +0 -150
  135. data/lib/rumale/tree/decision_tree_regressor.rb +0 -116
  136. data/lib/rumale/tree/extra_tree_classifier.rb +0 -107
  137. data/lib/rumale/tree/extra_tree_regressor.rb +0 -94
  138. data/lib/rumale/tree/gradient_tree_regressor.rb +0 -202
  139. data/lib/rumale/tree/node.rb +0 -39
  140. data/lib/rumale/utils.rb +0 -42
  141. data/lib/rumale/validation.rb +0 -128
  142. data/lib/rumale/values.rb +0 -13
@@ -1,155 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'rumale/base/base_estimator'
4
- require 'rumale/base/transformer'
5
- require 'rumale/utils'
6
- require 'rumale/pairwise_metric'
7
- require 'rumale/decomposition/pca'
8
-
9
- module Rumale
10
- module Manifold
11
- # MDS is a class that implements Metric Multidimensional Scaling (MDS)
12
- # with Scaling by MAjorizing a COmplicated Function (SMACOF) algorithm.
13
- #
14
- # @example
15
- # mds = Rumale::Manifold::MDS.new(init: 'pca', max_iter: 500, random_seed: 1)
16
- # representations = mds.fit_transform(samples)
17
- #
18
- # *Reference*
19
- # - Groenen, P J. F. and van de Velden, M., "Multidimensional Scaling by Majorization: A Review," J. of Statistical Software, Vol. 73 (8), 2016.
20
- class MDS
21
- include Base::BaseEstimator
22
- include Base::Transformer
23
-
24
- # Return the data in representation space.
25
- # @return [Numo::DFloat] (shape: [n_samples, n_components])
26
- attr_reader :embedding
27
-
28
- # Return the stress function value after optimization.
29
- # @return [Float]
30
- attr_reader :stress
31
-
32
- # Return the number of iterations run for optimization
33
- # @return [Integer]
34
- attr_reader :n_iter
35
-
36
- # Return the random generator.
37
- # @return [Random]
38
- attr_reader :rng
39
-
40
- # Create a new transformer with MDS.
41
- #
42
- # @param n_components [Integer] The number of dimensions on representation space.
43
- # @param metric [String] The metric to calculate the distances in original space.
44
- # If metric is 'euclidean', Euclidean distance is calculated for distance in original space.
45
- # If metric is 'precomputed', the fit and fit_transform methods expect to be given a distance matrix.
46
- # @param init [String] The init is a method to initialize the representaion space.
47
- # If init is 'random', the representaion space is initialized with normal random variables.
48
- # If init is 'pca', the result of principal component analysis as the initial value of the representation space.
49
- # @param max_iter [Integer] The maximum number of iterations.
50
- # @param tol [Float] The tolerance of stress value for terminating optimization.
51
- # If tol is nil, it does not use stress value as a criterion for terminating the optimization.
52
- # @param verbose [Boolean] The flag indicating whether to output stress value during iteration.
53
- # @param random_seed [Integer] The seed value using to initialize the random generator.
54
- def initialize(n_components: 2, metric: 'euclidean', init: 'random',
55
- max_iter: 300, tol: nil, verbose: false, random_seed: nil)
56
- check_params_numeric(n_components: n_components, max_iter: max_iter)
57
- check_params_string(metric: metric, init: init)
58
- check_params_boolean(verbose: verbose)
59
- check_params_numeric_or_nil(tol: tol, random_seed: random_seed)
60
- check_params_positive(n_components: n_components, max_iter: max_iter)
61
- @params = {}
62
- @params[:n_components] = n_components
63
- @params[:max_iter] = max_iter
64
- @params[:tol] = tol
65
- @params[:metric] = metric
66
- @params[:init] = init
67
- @params[:verbose] = verbose
68
- @params[:random_seed] = random_seed
69
- @params[:random_seed] ||= srand
70
- @rng = Random.new(@params[:random_seed])
71
- @embedding = nil
72
- @stress = nil
73
- @n_iter = nil
74
- end
75
-
76
- # Fit the model with given training data.
77
- #
78
- # @overload fit(x) -> MDS
79
- #
80
- # @param x [Numo::DFloat] (shape: [n_samples, n_features]) The training data to be used for fitting the model.
81
- # If the metric is 'precomputed', x must be a square distance matrix (shape: [n_samples, n_samples]).
82
- # @return [MDS] The learned transformer itself.
83
- def fit(x, _not_used = nil)
84
- x = check_convert_sample_array(x)
85
- raise ArgumentError, 'Expect the input distance matrix to be square.' if @params[:metric] == 'precomputed' && x.shape[0] != x.shape[1]
86
-
87
- # initialize some varibales.
88
- n_samples = x.shape[0]
89
- hi_distance_mat = @params[:metric] == 'precomputed' ? x : Rumale::PairwiseMetric.euclidean_distance(x)
90
- @embedding = init_embedding(x)
91
- lo_distance_mat = Rumale::PairwiseMetric.euclidean_distance(@embedding)
92
- @stress = calc_stress(hi_distance_mat, lo_distance_mat)
93
- @n_iter = 0
94
- # perform optimization.
95
- @params[:max_iter].times do |t|
96
- # guttman tarnsform.
97
- ratio = hi_distance_mat / lo_distance_mat
98
- ratio[ratio.diag_indices] = 0.0
99
- ratio[lo_distance_mat.eq(0)] = 0.0
100
- tmp_mat = -ratio
101
- tmp_mat[tmp_mat.diag_indices] += ratio.sum(axis: 1)
102
- @embedding = 1.fdiv(n_samples) * tmp_mat.dot(@embedding)
103
- lo_distance_mat = Rumale::PairwiseMetric.euclidean_distance(@embedding)
104
- # check convergence.
105
- new_stress = calc_stress(hi_distance_mat, lo_distance_mat)
106
- if terminate?(@stress, new_stress)
107
- @stress = new_stress
108
- break
109
- end
110
- # next step.
111
- @n_iter = t + 1
112
- @stress = new_stress
113
- puts "[MDS] stress function after #{@n_iter} iterations: #{@stress}" if @params[:verbose] && (@n_iter % 100).zero?
114
- end
115
- self
116
- end
117
-
118
- # Fit the model with training data, and then transform them with the learned model.
119
- #
120
- # @overload fit_transform(x) -> Numo::DFloat
121
- #
122
- # @param x [Numo::DFloat] (shape: [n_samples, n_features]) The training data to be used for fitting the model.
123
- # If the metric is 'precomputed', x must be a square distance matrix (shape: [n_samples, n_samples]).
124
- # @return [Numo::DFloat] (shape: [n_samples, n_components]) The transformed data
125
- def fit_transform(x, _not_used = nil)
126
- fit(x)
127
- @embedding.dup
128
- end
129
-
130
- private
131
-
132
- def init_embedding(x)
133
- if @params[:init] == 'pca' && @params[:metric] == 'euclidean'
134
- pca = Rumale::Decomposition::PCA.new(n_components: @params[:n_components], random_seed: @params[:random_seed])
135
- pca.fit_transform(x)
136
- else
137
- n_samples = x.shape[0]
138
- sub_rng = @rng.dup
139
- Rumale::Utils.rand_uniform([n_samples, @params[:n_components]], sub_rng) - 0.5
140
- end
141
- end
142
-
143
- def terminate?(old_stress, new_stress)
144
- return false if @params[:tol].nil?
145
- return false if old_stress.nil?
146
-
147
- (old_stress - new_stress).abs <= @params[:tol]
148
- end
149
-
150
- def calc_stress(hi_distance_mat, lo_distance_mat)
151
- ((hi_distance_mat - lo_distance_mat)**2).sum.fdiv(2)
152
- end
153
- end
154
- end
155
- end
@@ -1,222 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'rumale/base/base_estimator'
4
- require 'rumale/base/transformer'
5
- require 'rumale/utils'
6
- require 'rumale/pairwise_metric'
7
- require 'rumale/decomposition/pca'
8
-
9
- module Rumale
10
- # Module for data embedding algorithms.
11
- module Manifold
12
- # TSNE is a class that implements t-Distributed Stochastic Neighbor Embedding (t-SNE)
13
- # with fixed-point optimization algorithm.
14
- # Fixed-point algorithm usually converges faster than gradient descent method and
15
- # do not need the learning parameters such as the learning rate and momentum.
16
- #
17
- # @example
18
- # tsne = Rumale::Manifold::TSNE.new(perplexity: 40.0, init: 'pca', max_iter: 500, random_seed: 1)
19
- # representations = tsne.fit_transform(samples)
20
- #
21
- # *Reference*
22
- # - van der Maaten, L., and Hinton, G., "Visualizing data using t-SNE," J. of Machine Learning Research, vol. 9, pp. 2579--2605, 2008.
23
- # - Yang, Z., King, I., Xu, Z., and Oja, E., "Heavy-Tailed Symmetric Stochastic Neighbor Embedding," Proc. NIPS'09, pp. 2169--2177, 2009.
24
- class TSNE
25
- include Base::BaseEstimator
26
- include Base::Transformer
27
-
28
- # Return the data in representation space.
29
- # @return [Numo::DFloat] (shape: [n_samples, n_components])
30
- attr_reader :embedding
31
-
32
- # Return the Kullback-Leibler divergence after optimization.
33
- # @return [Float]
34
- attr_reader :kl_divergence
35
-
36
- # Return the number of iterations run for optimization
37
- # @return [Integer]
38
- attr_reader :n_iter
39
-
40
- # Return the random generator.
41
- # @return [Random]
42
- attr_reader :rng
43
-
44
- # Create a new transformer with t-SNE.
45
- #
46
- # @param n_components [Integer] The number of dimensions on representation space.
47
- # @param perplexity [Float] The effective number of neighbors for each point. Perplexity are typically set from 5 to 50.
48
- # @param metric [String] The metric to calculate the distances in original space.
49
- # If metric is 'euclidean', Euclidean distance is calculated for distance in original space.
50
- # If metric is 'precomputed', the fit and fit_transform methods expect to be given a distance matrix.
51
- # @param init [String] The init is a method to initialize the representaion space.
52
- # If init is 'random', the representaion space is initialized with normal random variables.
53
- # If init is 'pca', the result of principal component analysis as the initial value of the representation space.
54
- # @param max_iter [Integer] The maximum number of iterations.
55
- # @param tol [Float] The tolerance of KL-divergence for terminating optimization.
56
- # If tol is nil, it does not use KL divergence as a criterion for terminating the optimization.
57
- # @param verbose [Boolean] The flag indicating whether to output KL divergence during iteration.
58
- # @param random_seed [Integer] The seed value using to initialize the random generator.
59
- def initialize(n_components: 2, perplexity: 30.0, metric: 'euclidean', init: 'random',
60
- max_iter: 500, tol: nil, verbose: false, random_seed: nil)
61
- check_params_numeric(n_components: n_components, max_iter: max_iter, perplexity: perplexity)
62
- check_params_string(metric: metric, init: init)
63
- check_params_boolean(verbose: verbose)
64
- check_params_numeric_or_nil(tol: tol, random_seed: random_seed)
65
- check_params_positive(n_components: n_components, perplexity: perplexity, max_iter: max_iter)
66
- @params = {}
67
- @params[:n_components] = n_components
68
- @params[:perplexity] = perplexity
69
- @params[:max_iter] = max_iter
70
- @params[:tol] = tol
71
- @params[:metric] = metric
72
- @params[:init] = init
73
- @params[:verbose] = verbose
74
- @params[:random_seed] = random_seed
75
- @params[:random_seed] ||= srand
76
- @rng = Random.new(@params[:random_seed])
77
- @embedding = nil
78
- @kl_divergence = nil
79
- @n_iter = nil
80
- end
81
-
82
- # Fit the model with given training data.
83
- #
84
- # @overload fit(x) -> TSNE
85
- #
86
- # @param x [Numo::DFloat] (shape: [n_samples, n_features]) The training data to be used for fitting the model.
87
- # If the metric is 'precomputed', x must be a square distance matrix (shape: [n_samples, n_samples]).
88
- # @return [TSNE] The learned transformer itself.
89
- def fit(x, _not_used = nil)
90
- x = check_convert_sample_array(x)
91
- raise ArgumentError, 'Expect the input distance matrix to be square.' if @params[:metric] == 'precomputed' && x.shape[0] != x.shape[1]
92
-
93
- # initialize some varibales.
94
- @n_iter = 0
95
- distance_mat = @params[:metric] == 'precomputed' ? x**2 : Rumale::PairwiseMetric.squared_error(x)
96
- hi_prob_mat = gaussian_distributed_probability_matrix(distance_mat)
97
- y = init_embedding(x)
98
- lo_prob_mat = t_distributed_probability_matrix(y)
99
- # perform fixed-point optimization.
100
- one_vec = Numo::DFloat.ones(x.shape[0]).expand_dims(1)
101
- @params[:max_iter].times do |t|
102
- break if terminate?(hi_prob_mat, lo_prob_mat)
103
-
104
- a = hi_prob_mat * lo_prob_mat
105
- b = lo_prob_mat**2
106
- y = (b.dot(one_vec) * y + (a - b).dot(y)) / a.dot(one_vec)
107
- lo_prob_mat = t_distributed_probability_matrix(y)
108
- @n_iter = t + 1
109
- if @params[:verbose] && (@n_iter % 100).zero?
110
- puts "[t-SNE] KL divergence after #{@n_iter} iterations: #{cost(hi_prob_mat, lo_prob_mat)}"
111
- end
112
- end
113
- # store results.
114
- @embedding = y
115
- @kl_divergence = cost(hi_prob_mat, lo_prob_mat)
116
- self
117
- end
118
-
119
- # Fit the model with training data, and then transform them with the learned model.
120
- #
121
- # @overload fit_transform(x) -> Numo::DFloat
122
- #
123
- # @param x [Numo::DFloat] (shape: [n_samples, n_features]) The training data to be used for fitting the model.
124
- # If the metric is 'precomputed', x must be a square distance matrix (shape: [n_samples, n_samples]).
125
- # @return [Numo::DFloat] (shape: [n_samples, n_components]) The transformed data
126
- def fit_transform(x, _not_used = nil)
127
- fit(x)
128
- @embedding.dup
129
- end
130
-
131
- private
132
-
133
- def init_embedding(x)
134
- if @params[:init] == 'pca' && @params[:metric] == 'euclidean'
135
- pca = Rumale::Decomposition::PCA.new(n_components: @params[:n_components], random_seed: @params[:random_seed])
136
- pca.fit_transform(x)
137
- else
138
- n_samples = x.shape[0]
139
- sub_rng = @rng.dup
140
- Rumale::Utils.rand_normal([n_samples, @params[:n_components]], sub_rng, 0, 0.0001)
141
- end
142
- end
143
-
144
- def gaussian_distributed_probability_matrix(distance_mat)
145
- # initialize some variables.
146
- n_samples = distance_mat.shape[0]
147
- prob_mat = Numo::DFloat.zeros(n_samples, n_samples)
148
- sum_beta = 0.0
149
- # calculate conditional probabilities.
150
- n_samples.times do |n|
151
- beta, probs = optimal_probabilities(n, distance_mat[n, true])
152
- prob_mat[n, true] = probs
153
- sum_beta += beta
154
- puts "[t-SNE] Computed conditional probabilities for sample #{n + 1} / #{n_samples}" if @params[:verbose] && ((n + 1) % 1000).zero?
155
- end
156
- puts "[t-SNE] Mean sigma: #{Math.sqrt(n_samples.fdiv(sum_beta))}" if @params[:verbose]
157
- # symmetrize and normalize probability matrix.
158
- prob_mat[prob_mat.diag_indices(0)] = 0.0
159
- prob_mat = 0.5 * (prob_mat + prob_mat.transpose)
160
- prob_mat / prob_mat.sum
161
- end
162
-
163
- def optimal_probabilities(sample_id, distance_vec, max_iter = 100)
164
- # initialize some variables.
165
- probs = nil
166
- beta = 1.0
167
- betamin = Float::MIN
168
- betamax = Float::MAX
169
- init_entropy = Math.log(@params[:perplexity])
170
- # calculate optimal beta and conditional probabilities with binary search.
171
- max_iter.times do
172
- entropy, probs = gaussian_distributed_probability_vector(sample_id, distance_vec, beta)
173
- diff_entropy = entropy - init_entropy
174
- break if diff_entropy.abs <= 1e-5
175
-
176
- if diff_entropy.positive?
177
- betamin = beta
178
- if betamax == Float::MAX
179
- beta *= 2.0
180
- else
181
- beta = 0.5 * (beta + betamax)
182
- end
183
- else
184
- betamax = beta
185
- if betamin == Float::MIN
186
- beta /= 2.0
187
- else
188
- beta = 0.5 * (beta + betamin)
189
- end
190
- end
191
- end
192
- [beta, probs]
193
- end
194
-
195
- def gaussian_distributed_probability_vector(n, distance_vec, beta)
196
- probs = Numo::NMath.exp(-beta * distance_vec)
197
- probs[n] = 0.0
198
- sum_probs = probs.sum
199
- probs /= sum_probs
200
- entropy = Math.log(sum_probs) + beta * (distance_vec * probs).sum
201
- [entropy, probs]
202
- end
203
-
204
- def t_distributed_probability_matrix(y)
205
- distance_mat = Rumale::PairwiseMetric.squared_error(y)
206
- prob_mat = 1.0 / (1.0 + distance_mat)
207
- prob_mat[prob_mat.diag_indices(0)] = 0.0
208
- prob_mat / prob_mat.sum
209
- end
210
-
211
- def cost(p, q)
212
- (p * Numo::NMath.log(Numo::DFloat.maximum(1e-20, p) / Numo::DFloat.maximum(1e-20, q))).sum
213
- end
214
-
215
- def terminate?(p, q)
216
- return false if @params[:tol].nil?
217
-
218
- cost(p, q) <= @params[:tol]
219
- end
220
- end
221
- end
222
- end
@@ -1,113 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'rumale/base/base_estimator'
4
- require 'rumale/base/transformer'
5
-
6
- module Rumale
7
- # Module for metric learning algorithms.
8
- module MetricLearning
9
- # FisherDiscriminantAnalysis is a class that implements Fisher Discriminant Analysis.
10
- #
11
- # @example
12
- # transformer = Rumale::MetricLearning::FisherDiscriminantAnalysis.new
13
- # transformer.fit(training_samples, traininig_labels)
14
- # low_samples = transformer.transform(testing_samples)
15
- #
16
- # *Reference*
17
- # - Fisher, R. A., "The use of multiple measurements in taxonomic problems," Annals of Eugenics, vol. 7, pp. 179--188, 1936.
18
- # - Sugiyama, M., "Local Fisher Discriminant Analysis for Supervised Dimensionality Reduction," Proc. ICML'06, pp. 905--912, 2006.
19
- class FisherDiscriminantAnalysis
20
- include Base::BaseEstimator
21
- include Base::Transformer
22
-
23
- # Returns the transform matrix.
24
- # @return [Numo::DFloat] (shape: [n_components, n_features])
25
- attr_reader :components
26
-
27
- # Returns the mean vector.
28
- # @return [Numo::DFloat] (shape: [n_features])
29
- attr_reader :mean
30
-
31
- # Returns the class mean vectors.
32
- # @return [Numo::DFloat] (shape: [n_classes, n_features])
33
- attr_reader :class_means
34
-
35
- # Return the class labels.
36
- # @return [Numo::Int32] (shape: [n_classes])
37
- attr_reader :classes
38
-
39
- # Create a new transformer with FisherDiscriminantAnalysis.
40
- #
41
- # @param n_components [Integer] The number of components.
42
- # If nil is given, the number of components will be set to [n_features, n_classes - 1].min
43
- def initialize(n_components: nil)
44
- check_params_numeric_or_nil(n_components: n_components)
45
- @params = {}
46
- @params[:n_components] = n_components
47
- end
48
-
49
- # Fit the model with given training data.
50
- #
51
- # @param x [Numo::DFloat] (shape: [n_samples, n_features]) The training data to be used for fitting the model.
52
- # @param y [Numo::Int32] (shape: [n_samples]) The labels to be used for fitting the model.
53
- # @return [FisherDiscriminantAnalysis] The learned classifier itself.
54
- def fit(x, y)
55
- x = check_convert_sample_array(x)
56
- y = check_convert_label_array(y)
57
- check_sample_label_size(x, y)
58
- raise 'FisherDiscriminatAnalysis#fit requires Numo::Linalg but that is not loaded.' unless enable_linalg?
59
-
60
- # initialize some variables.
61
- n_features = x.shape[1]
62
- @classes = Numo::Int32[*y.to_a.uniq.sort]
63
- n_classes = @classes.size
64
- n_components = if @params[:n_components].nil?
65
- [n_features, n_classes - 1].min
66
- else
67
- [n_features, @params[:n_components]].min
68
- end
69
-
70
- # calculate within and between scatter matricies.
71
- within_mat = Numo::DFloat.zeros(n_features, n_features)
72
- between_mat = Numo::DFloat.zeros(n_features, n_features)
73
- @class_means = Numo::DFloat.zeros(n_classes, n_features)
74
- @mean = x.mean(0)
75
- @classes.each_with_index do |label, i|
76
- mask_vec = y.eq(label)
77
- sz_class = mask_vec.count
78
- class_samples = x[mask_vec, true]
79
- class_mean = class_samples.mean(0)
80
- within_mat += (class_samples - class_mean).transpose.dot(class_samples - class_mean)
81
- between_mat += sz_class * (class_mean - @mean).expand_dims(1) * (class_mean - @mean)
82
- @class_means[i, true] = class_mean
83
- end
84
-
85
- # calculate components.
86
- _, evecs = Numo::Linalg.eigh(between_mat, within_mat, vals_range: (n_features - n_components)...n_features)
87
- comps = evecs.reverse(1).transpose.dup
88
- @components = n_components == 1 ? comps[0, true].dup : comps.dup
89
- self
90
- end
91
-
92
- # Fit the model with training data, and then transform them with the learned model.
93
- #
94
- # @param x [Numo::DFloat] (shape: [n_samples, n_features]) The training data to be used for fitting the model.
95
- # @param y [Numo::Int32] (shape: [n_samples]) The labels to be used for fitting the model.
96
- # @return [Numo::DFloat] (shape: [n_samples, n_components]) The transformed data
97
- def fit_transform(x, y)
98
- x = check_convert_sample_array(x)
99
- y = check_convert_label_array(y)
100
- fit(x, y).transform(x)
101
- end
102
-
103
- # Transform the given data with the learned model.
104
- #
105
- # @param x [Numo::DFloat] (shape: [n_samples, n_features]) The data to be transformed with the learned model.
106
- # @return [Numo::DFloat] (shape: [n_samples, n_components]) The transformed data.
107
- def transform(x)
108
- x = check_convert_sample_array(x)
109
- x.dot(@components.transpose)
110
- end
111
- end
112
- end
113
- end
@@ -1,161 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'rumale/base/base_estimator'
4
- require 'rumale/base/transformer'
5
- require 'rumale/decomposition/pca'
6
- require 'rumale/pairwise_metric'
7
- require 'rumale/utils'
8
- require 'lbfgsb'
9
-
10
- module Rumale
11
- module MetricLearning
12
- # MLKR is a class that implements Metric Learning for Kernel Regression.
13
- #
14
- # @example
15
- # transformer = Rumale::MetricLearning::MLKR.new
16
- # transformer.fit(training_samples, traininig_target_values)
17
- # low_samples = transformer.transform(testing_samples)
18
- #
19
- # *Reference*
20
- # - Weinberger, K. Q. and Tesauro, G., "Metric Learning for Kernel Regression," Proc. AISTATS'07, pp. 612--629, 2007.
21
- class MLKR
22
- include Base::BaseEstimator
23
- include Base::Transformer
24
-
25
- # Returns the metric components.
26
- # @return [Numo::DFloat] (shape: [n_components, n_features])
27
- attr_reader :components
28
-
29
- # Return the number of iterations run for optimization
30
- # @return [Integer]
31
- attr_reader :n_iter
32
-
33
- # Return the random generator.
34
- # @return [Random]
35
- attr_reader :rng
36
-
37
- # Create a new transformer with MLKR.
38
- #
39
- # @param n_components [Integer] The number of components.
40
- # @param init [String] The initialization method for components ('random' or 'pca').
41
- # @param max_iter [Integer] The maximum number of iterations.
42
- # @param tol [Float] The tolerance of termination criterion.
43
- # This value is given as tol / Lbfgsb::DBL_EPSILON to the factr argument of Lbfgsb.minimize method.
44
- # @param verbose [Boolean] The flag indicating whether to output loss during iteration.
45
- # If true is given, 'iterate.dat' file is generated by lbfgsb.rb.
46
- # @param random_seed [Integer] The seed value using to initialize the random generator.
47
- def initialize(n_components: nil, init: 'random', max_iter: 100, tol: 1e-6, verbose: false, random_seed: nil)
48
- check_params_numeric_or_nil(n_components: n_components, random_seed: random_seed)
49
- check_params_numeric(max_iter: max_iter, tol: tol)
50
- check_params_string(init: init)
51
- check_params_boolean(verbose: verbose)
52
- @params = {}
53
- @params[:n_components] = n_components
54
- @params[:init] = init
55
- @params[:max_iter] = max_iter
56
- @params[:tol] = tol
57
- @params[:verbose] = verbose
58
- @params[:random_seed] = random_seed
59
- @params[:random_seed] ||= srand
60
- @components = nil
61
- @n_iter = nil
62
- @rng = Random.new(@params[:random_seed])
63
- end
64
-
65
- # Fit the model with given training data.
66
- #
67
- # @param x [Numo::DFloat] (shape: [n_samples, n_features]) The training data to be used for fitting the model.
68
- # @param y [Numo::DFloat] (shape: [n_samples]) The target values to be used for fitting the model.
69
- # @return [MLKR] The learned classifier itself.
70
- def fit(x, y)
71
- x = check_convert_sample_array(x)
72
- y = check_convert_tvalue_array(y)
73
- check_sample_tvalue_size(x, y)
74
- n_features = x.shape[1]
75
- n_components = if @params[:n_components].nil?
76
- n_features
77
- else
78
- [n_features, @params[:n_components]].min
79
- end
80
- @components, @n_iter = optimize_components(x, y, n_features, n_components)
81
- @prototypes = x.dot(@components.transpose)
82
- @values = y
83
- self
84
- end
85
-
86
- # Fit the model with training data, and then transform them with the learned model.
87
- #
88
- # @param x [Numo::DFloat] (shape: [n_samples, n_features]) The training data to be used for fitting the model.
89
- # @param y [Numo::DFloat] (shape: [n_samples]) The target values to be used for fitting the model.
90
- # @return [Numo::DFloat] (shape: [n_samples, n_components]) The transformed data
91
- def fit_transform(x, y)
92
- x = check_convert_sample_array(x)
93
- y = check_convert_tvalue_array(y)
94
- check_sample_tvalue_size(x, y)
95
- fit(x, y).transform(x)
96
- end
97
-
98
- # Transform the given data with the learned model.
99
- #
100
- # @param x [Numo::DFloat] (shape: [n_samples, n_features]) The data to be transformed with the learned model.
101
- # @return [Numo::DFloat] (shape: [n_samples, n_components]) The transformed data.
102
- def transform(x)
103
- x = check_convert_sample_array(x)
104
- x.dot(@components.transpose)
105
- end
106
-
107
- private
108
-
109
- def init_components(x, n_features, n_components)
110
- if @params[:init] == 'pca'
111
- pca = Rumale::Decomposition::PCA.new(n_components: n_components)
112
- pca.fit(x).components.flatten.dup
113
- else
114
- Rumale::Utils.rand_normal([n_features, n_components], @rng.dup).flatten.dup
115
- end
116
- end
117
-
118
- def optimize_components(x, y, n_features, n_components)
119
- # initialize components.
120
- comp_init = init_components(x, n_features, n_components)
121
- # initialize optimization results.
122
- res = {}
123
- res[:x] = comp_init
124
- res[:n_iter] = 0
125
- # perform optimization.
126
- verbose = @params[:verbose] ? 1 : -1
127
- res = Lbfgsb.minimize(
128
- fnc: method(:mlkr_fnc), jcb: true, x_init: comp_init, args: [x, y],
129
- maxiter: @params[:max_iter], factr: @params[:tol] / Lbfgsb::DBL_EPSILON, verbose: verbose
130
- )
131
- # return the results.
132
- n_iter = res[:n_iter]
133
- comps = n_components == 1 ? res[:x].dup : res[:x].reshape(n_components, n_features)
134
- [comps, n_iter]
135
- end
136
-
137
- def mlkr_fnc(w, x, y)
138
- # initialize some variables.
139
- n_features = x.shape[1]
140
- n_components = w.size / n_features
141
- # projection.
142
- w = w.reshape(n_components, n_features)
143
- z = x.dot(w.transpose)
144
- # predict values.
145
- kernel_mat = Numo::NMath.exp(-Rumale::PairwiseMetric.squared_error(z))
146
- kernel_mat[kernel_mat.diag_indices] = 0.0
147
- norm = kernel_mat.sum(1)
148
- norm[norm.eq(0)] = 1
149
- y_pred = kernel_mat.dot(y) / norm
150
- # calculate loss.
151
- y_diff = y_pred - y
152
- loss = (y_diff**2).sum
153
- # calculate gradient.
154
- weight_mat = y_diff * y_diff.expand_dims(1) * kernel_mat
155
- weight_mat = weight_mat.sum(0).diag - weight_mat
156
- gradient = 8 * z.transpose.dot(weight_mat).dot(x)
157
- [loss, gradient.flatten.dup]
158
- end
159
- end
160
- end
161
- end