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.
- checksums.yaml +4 -4
- data/LICENSE.txt +5 -1
- data/README.md +3 -288
- data/lib/rumale/version.rb +1 -1
- data/lib/rumale.rb +20 -131
- metadata +252 -150
- data/CHANGELOG.md +0 -643
- data/CODE_OF_CONDUCT.md +0 -74
- data/ext/rumale/extconf.rb +0 -37
- data/ext/rumale/rumaleext.c +0 -545
- data/ext/rumale/rumaleext.h +0 -12
- data/lib/rumale/base/base_estimator.rb +0 -49
- data/lib/rumale/base/classifier.rb +0 -36
- data/lib/rumale/base/cluster_analyzer.rb +0 -31
- data/lib/rumale/base/evaluator.rb +0 -17
- data/lib/rumale/base/regressor.rb +0 -36
- data/lib/rumale/base/splitter.rb +0 -21
- data/lib/rumale/base/transformer.rb +0 -22
- data/lib/rumale/clustering/dbscan.rb +0 -123
- data/lib/rumale/clustering/gaussian_mixture.rb +0 -218
- data/lib/rumale/clustering/hdbscan.rb +0 -291
- data/lib/rumale/clustering/k_means.rb +0 -122
- data/lib/rumale/clustering/k_medoids.rb +0 -141
- data/lib/rumale/clustering/mini_batch_k_means.rb +0 -139
- data/lib/rumale/clustering/power_iteration.rb +0 -127
- data/lib/rumale/clustering/single_linkage.rb +0 -203
- data/lib/rumale/clustering/snn.rb +0 -76
- data/lib/rumale/clustering/spectral_clustering.rb +0 -115
- data/lib/rumale/dataset.rb +0 -246
- data/lib/rumale/decomposition/factor_analysis.rb +0 -150
- data/lib/rumale/decomposition/fast_ica.rb +0 -188
- data/lib/rumale/decomposition/nmf.rb +0 -124
- data/lib/rumale/decomposition/pca.rb +0 -159
- data/lib/rumale/ensemble/ada_boost_classifier.rb +0 -179
- data/lib/rumale/ensemble/ada_boost_regressor.rb +0 -160
- data/lib/rumale/ensemble/extra_trees_classifier.rb +0 -139
- data/lib/rumale/ensemble/extra_trees_regressor.rb +0 -125
- data/lib/rumale/ensemble/gradient_boosting_classifier.rb +0 -306
- data/lib/rumale/ensemble/gradient_boosting_regressor.rb +0 -237
- data/lib/rumale/ensemble/random_forest_classifier.rb +0 -189
- data/lib/rumale/ensemble/random_forest_regressor.rb +0 -153
- data/lib/rumale/ensemble/stacking_classifier.rb +0 -215
- data/lib/rumale/ensemble/stacking_regressor.rb +0 -163
- data/lib/rumale/ensemble/voting_classifier.rb +0 -126
- data/lib/rumale/ensemble/voting_regressor.rb +0 -82
- data/lib/rumale/evaluation_measure/accuracy.rb +0 -29
- data/lib/rumale/evaluation_measure/adjusted_rand_score.rb +0 -74
- data/lib/rumale/evaluation_measure/calinski_harabasz_score.rb +0 -56
- data/lib/rumale/evaluation_measure/davies_bouldin_score.rb +0 -53
- data/lib/rumale/evaluation_measure/explained_variance_score.rb +0 -39
- data/lib/rumale/evaluation_measure/f_score.rb +0 -50
- data/lib/rumale/evaluation_measure/function.rb +0 -147
- data/lib/rumale/evaluation_measure/log_loss.rb +0 -45
- data/lib/rumale/evaluation_measure/mean_absolute_error.rb +0 -29
- data/lib/rumale/evaluation_measure/mean_squared_error.rb +0 -29
- data/lib/rumale/evaluation_measure/mean_squared_log_error.rb +0 -29
- data/lib/rumale/evaluation_measure/median_absolute_error.rb +0 -30
- data/lib/rumale/evaluation_measure/mutual_information.rb +0 -49
- data/lib/rumale/evaluation_measure/normalized_mutual_information.rb +0 -53
- data/lib/rumale/evaluation_measure/precision.rb +0 -50
- data/lib/rumale/evaluation_measure/precision_recall.rb +0 -96
- data/lib/rumale/evaluation_measure/purity.rb +0 -40
- data/lib/rumale/evaluation_measure/r2_score.rb +0 -43
- data/lib/rumale/evaluation_measure/recall.rb +0 -50
- data/lib/rumale/evaluation_measure/roc_auc.rb +0 -130
- data/lib/rumale/evaluation_measure/silhouette_score.rb +0 -82
- data/lib/rumale/feature_extraction/feature_hasher.rb +0 -110
- data/lib/rumale/feature_extraction/hash_vectorizer.rb +0 -155
- data/lib/rumale/feature_extraction/tfidf_transformer.rb +0 -113
- data/lib/rumale/kernel_approximation/nystroem.rb +0 -126
- data/lib/rumale/kernel_approximation/rbf.rb +0 -102
- data/lib/rumale/kernel_machine/kernel_fda.rb +0 -120
- data/lib/rumale/kernel_machine/kernel_pca.rb +0 -97
- data/lib/rumale/kernel_machine/kernel_ridge.rb +0 -82
- data/lib/rumale/kernel_machine/kernel_ridge_classifier.rb +0 -92
- data/lib/rumale/kernel_machine/kernel_svc.rb +0 -193
- data/lib/rumale/linear_model/base_sgd.rb +0 -285
- data/lib/rumale/linear_model/elastic_net.rb +0 -119
- data/lib/rumale/linear_model/lasso.rb +0 -115
- data/lib/rumale/linear_model/linear_regression.rb +0 -201
- data/lib/rumale/linear_model/logistic_regression.rb +0 -275
- data/lib/rumale/linear_model/nnls.rb +0 -137
- data/lib/rumale/linear_model/ridge.rb +0 -209
- data/lib/rumale/linear_model/svc.rb +0 -213
- data/lib/rumale/linear_model/svr.rb +0 -132
- data/lib/rumale/manifold/mds.rb +0 -155
- data/lib/rumale/manifold/tsne.rb +0 -222
- data/lib/rumale/metric_learning/fisher_discriminant_analysis.rb +0 -113
- data/lib/rumale/metric_learning/mlkr.rb +0 -161
- data/lib/rumale/metric_learning/neighbourhood_component_analysis.rb +0 -167
- data/lib/rumale/model_selection/cross_validation.rb +0 -125
- data/lib/rumale/model_selection/function.rb +0 -42
- data/lib/rumale/model_selection/grid_search_cv.rb +0 -225
- data/lib/rumale/model_selection/group_k_fold.rb +0 -93
- data/lib/rumale/model_selection/group_shuffle_split.rb +0 -115
- data/lib/rumale/model_selection/k_fold.rb +0 -81
- data/lib/rumale/model_selection/shuffle_split.rb +0 -90
- data/lib/rumale/model_selection/stratified_k_fold.rb +0 -99
- data/lib/rumale/model_selection/stratified_shuffle_split.rb +0 -118
- data/lib/rumale/model_selection/time_series_split.rb +0 -91
- data/lib/rumale/multiclass/one_vs_rest_classifier.rb +0 -83
- data/lib/rumale/naive_bayes/base_naive_bayes.rb +0 -47
- data/lib/rumale/naive_bayes/bernoulli_nb.rb +0 -82
- data/lib/rumale/naive_bayes/complement_nb.rb +0 -85
- data/lib/rumale/naive_bayes/gaussian_nb.rb +0 -69
- data/lib/rumale/naive_bayes/multinomial_nb.rb +0 -74
- data/lib/rumale/naive_bayes/negation_nb.rb +0 -71
- data/lib/rumale/nearest_neighbors/k_neighbors_classifier.rb +0 -133
- data/lib/rumale/nearest_neighbors/k_neighbors_regressor.rb +0 -108
- data/lib/rumale/nearest_neighbors/vp_tree.rb +0 -132
- data/lib/rumale/neural_network/adam.rb +0 -56
- data/lib/rumale/neural_network/base_mlp.rb +0 -248
- data/lib/rumale/neural_network/mlp_classifier.rb +0 -120
- data/lib/rumale/neural_network/mlp_regressor.rb +0 -90
- data/lib/rumale/pairwise_metric.rb +0 -152
- data/lib/rumale/pipeline/feature_union.rb +0 -69
- data/lib/rumale/pipeline/pipeline.rb +0 -175
- data/lib/rumale/preprocessing/bin_discretizer.rb +0 -93
- data/lib/rumale/preprocessing/binarizer.rb +0 -60
- data/lib/rumale/preprocessing/kernel_calculator.rb +0 -92
- data/lib/rumale/preprocessing/l1_normalizer.rb +0 -62
- data/lib/rumale/preprocessing/l2_normalizer.rb +0 -63
- data/lib/rumale/preprocessing/label_binarizer.rb +0 -89
- data/lib/rumale/preprocessing/label_encoder.rb +0 -79
- data/lib/rumale/preprocessing/max_abs_scaler.rb +0 -61
- data/lib/rumale/preprocessing/max_normalizer.rb +0 -62
- data/lib/rumale/preprocessing/min_max_scaler.rb +0 -76
- data/lib/rumale/preprocessing/one_hot_encoder.rb +0 -100
- data/lib/rumale/preprocessing/ordinal_encoder.rb +0 -109
- data/lib/rumale/preprocessing/polynomial_features.rb +0 -109
- data/lib/rumale/preprocessing/standard_scaler.rb +0 -71
- data/lib/rumale/probabilistic_output.rb +0 -114
- data/lib/rumale/tree/base_decision_tree.rb +0 -150
- data/lib/rumale/tree/decision_tree_classifier.rb +0 -150
- data/lib/rumale/tree/decision_tree_regressor.rb +0 -116
- data/lib/rumale/tree/extra_tree_classifier.rb +0 -107
- data/lib/rumale/tree/extra_tree_regressor.rb +0 -94
- data/lib/rumale/tree/gradient_tree_regressor.rb +0 -202
- data/lib/rumale/tree/node.rb +0 -39
- data/lib/rumale/utils.rb +0 -42
- data/lib/rumale/validation.rb +0 -128
- data/lib/rumale/values.rb +0 -13
data/lib/rumale/manifold/mds.rb
DELETED
|
@@ -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
|
data/lib/rumale/manifold/tsne.rb
DELETED
|
@@ -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
|