Class: Rumale::Manifold::MDS
- Inherits:
-
Object
- Object
- Rumale::Manifold::MDS
- Includes:
- Base::BaseEstimator, Base::Transformer
- Defined in:
- lib/rumale/manifold/mds.rb
Overview
MDS is a class that implements Metric Multidimensional Scaling (MDS) with Scaling by MAjorizing a COmplicated Function (SMACOF) algorithm.
Reference
-
P J. F. Groenen and M. van de Velden, “Multidimensional Scaling by Majorization: A Review,” J. of Statistical Software, Vol. 73 (8), 2016.
Instance Attribute Summary collapse
-
#embedding ⇒ Numo::DFloat
readonly
Return the data in representation space.
-
#n_iter ⇒ Integer
readonly
Return the number of iterations run for optimization.
-
#rng ⇒ Random
readonly
Return the random generator.
-
#stress ⇒ Float
readonly
Return the stress function value after optimization.
Attributes included from Base::BaseEstimator
Instance Method Summary collapse
-
#fit(x) ⇒ MDS
Fit the model with given training data.
-
#fit_transform(x) ⇒ Numo::DFloat
Fit the model with training data, and then transform them with the learned model.
-
#initialize(n_components: 2, metric: 'euclidean', init: 'random', max_iter: 300, tol: nil, verbose: false, random_seed: nil) ⇒ MDS
constructor
Create a new transformer with MDS.
-
#marshal_dump ⇒ Hash
Dump marshal data.
-
#marshal_load(obj) ⇒ nil
Load marshal data.
Constructor Details
#initialize(n_components: 2, metric: 'euclidean', init: 'random', max_iter: 300, tol: nil, verbose: false, random_seed: nil) ⇒ MDS
Create a new transformer with MDS.
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/rumale/manifold/mds.rb', line 54 def initialize(n_components: 2, metric: 'euclidean', init: 'random', max_iter: 300, tol: nil, verbose: false, random_seed: nil) check_params_integer(n_components: n_components, max_iter: max_iter) check_params_string(metric: metric, init: init) check_params_boolean(verbose: verbose) check_params_type_or_nil(Float, tol: tol) check_params_type_or_nil(Integer, random_seed: random_seed) check_params_positive(n_components: n_components, max_iter: max_iter) @params = {} @params[:n_components] = n_components @params[:max_iter] = max_iter @params[:tol] = tol @params[:metric] = metric @params[:init] = init @params[:verbose] = verbose @params[:random_seed] = random_seed @params[:random_seed] ||= srand @rng = Random.new(@params[:random_seed]) @embedding = nil @stress = nil @n_iter = nil end |
Instance Attribute Details
#embedding ⇒ Numo::DFloat (readonly)
Return the data in representation space.
26 27 28 |
# File 'lib/rumale/manifold/mds.rb', line 26 def @embedding end |
#n_iter ⇒ Integer (readonly)
Return the number of iterations run for optimization
34 35 36 |
# File 'lib/rumale/manifold/mds.rb', line 34 def n_iter @n_iter end |
#rng ⇒ Random (readonly)
Return the random generator.
38 39 40 |
# File 'lib/rumale/manifold/mds.rb', line 38 def rng @rng end |
#stress ⇒ Float (readonly)
Return the stress function value after optimization.
30 31 32 |
# File 'lib/rumale/manifold/mds.rb', line 30 def stress @stress end |
Instance Method Details
#fit(x) ⇒ MDS
Fit the model with given training data.
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/rumale/manifold/mds.rb', line 84 def fit(x, _not_used = nil) check_sample_array(x) raise ArgumentError, 'Expect the input distance matrix to be square.' if @params[:metric] == 'precomputed' && x.shape[0] != x.shape[1] # initialize some varibales. n_samples = x.shape[0] hi_distance_mat = @params[:metric] == 'precomputed' ? x : Rumale::PairwiseMetric.euclidean_distance(x) @embedding = (x) lo_distance_mat = Rumale::PairwiseMetric.euclidean_distance(@embedding) @stress = calc_stress(hi_distance_mat, lo_distance_mat) @n_iter = 0 # perform optimization. @params[:max_iter].times do |t| # guttman tarnsform. ratio = hi_distance_mat / lo_distance_mat ratio[ratio.diag_indices] = 0.0 ratio[lo_distance_mat.eq(0)] = 0.0 tmp_mat = -ratio tmp_mat[tmp_mat.diag_indices] += ratio.sum(axis: 1) @embedding = 1.fdiv(n_samples) * tmp_mat.dot(@embedding) # check convergence. new_stress = calc_stress(hi_distance_mat, lo_distance_mat) if terminate?(@stress, new_stress) @stress = new_stress break end # next step. @n_iter = t + 1 @stress = new_stress lo_distance_mat = Rumale::PairwiseMetric.euclidean_distance(@embedding) puts "[MDS] stress function after #{@n_iter} iterations: #{@stress}" if @params[:verbose] && (@n_iter % 100).zero? end self end |
#fit_transform(x) ⇒ Numo::DFloat
Fit the model with training data, and then transform them with the learned model.
125 126 127 128 |
# File 'lib/rumale/manifold/mds.rb', line 125 def fit_transform(x, _not_used = nil) fit(x) @embedding.dup end |
#marshal_dump ⇒ Hash
Dump marshal data.
132 133 134 135 136 137 138 |
# File 'lib/rumale/manifold/mds.rb', line 132 def marshal_dump { params: @params, embedding: @embedding, stress: @stress, n_iter: @n_iter, rng: @rng } end |
#marshal_load(obj) ⇒ nil
Load marshal data.
142 143 144 145 146 147 148 149 |
# File 'lib/rumale/manifold/mds.rb', line 142 def marshal_load(obj) @params = obj[:params] @embedding = obj[:embedding] @stress = obj[:stress] @n_iter = obj[:n_iter] @rng = obj[:rng] nil end |