Class: Rumale::Manifold::LocalTangentSpaceAlignment

Inherits:
Base::Estimator show all
Includes:
Base::Transformer
Defined in:
rumale-manifold/lib/rumale/manifold/local_tangent_space_alignment.rb

Overview

LocalTangentSpaceAlignment is a class that implements Local Tangent Space Alignment.

Reference

  • Zhang, A., and Zha, H., “Principal Manifolds and Nonlinear Diemnsion Reduction via Local Tangent Space Alignment,” SIAM Journal on Scientific Computing, vol. 26, iss. 1, pp. 313-338, 2004.

Examples:

require 'numo/linalg/autoloader'
require 'rumale/manifold/local_tangent_space_alignment'

lem = Rumale::Manifold::LocalTangentSpaceAlignment.new(n_components: 2, n_neighbors: 15)
z = lem.fit_transform(x)

Instance Attribute Summary collapse

Attributes inherited from Base::Estimator

#params

Instance Method Summary collapse

Constructor Details

#initialize(n_components: 2, n_neighbors: 10, reg_param: 1e-3) ⇒ LocalTangentSpaceAlignment

Create a new transformer with Local Tangent Space Alignment.

Parameters:

  • n_components (Integer) (defaults to: 2)

    The number of dimensions on representation space.

  • n_neighbors (Integer) (defaults to: 10)

    The number of nearest neighbors for finding k-nearest neighbors

  • reg_param (Float) (defaults to: 1e-3)

    The reguralization parameter for local gram matrix in transform method.



33
34
35
36
37
38
39
40
# File 'rumale-manifold/lib/rumale/manifold/local_tangent_space_alignment.rb', line 33

def initialize(n_components: 2, n_neighbors: 10, reg_param: 1e-3)
  super()
  @params = {
    n_components: n_components,
    n_neighbors: [1, n_neighbors].max,
    reg_param: reg_param
  }
end

Instance Attribute Details

#embeddingNumo::DFloat (readonly)

Return the data in representation space.

Returns:

  • (Numo::DFloat)

    (shape: [n_samples, n_components])



26
27
28
# File 'rumale-manifold/lib/rumale/manifold/local_tangent_space_alignment.rb', line 26

def embedding
  @embedding
end

Instance Method Details

#fit(x) ⇒ LocalTangentSpaceAlignment

Fit the model with given training data.

Returns The learned transformer itself.

Parameters:

  • x (Numo::DFloat)

    (shape: [n_samples, n_features]) The training data to be used for fitting the model.

Returns:



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'rumale-manifold/lib/rumale/manifold/local_tangent_space_alignment.rb', line 47

def fit(x, _y = nil)
  unless enable_linalg?(warning: false)
    raise 'LocalTangentSpaceAlignment#fit requires Numo::Linalg but that is not loaded'
  end

  x = Rumale::Validation.check_convert_sample_array(x)

  n_samples = x.shape[0]
  distance_mat = Rumale::PairwiseMetric.squared_error(x)
  neighbor_ids = neighbor_ids(distance_mat, @params[:n_neighbors], true)

  affinity_mat = Numo::DFloat.zeros(n_samples, n_samples)
  x_tangent = Numo::DFloat.zeros(@params[:n_neighbors], @params[:n_components] + 1)
  x_tangent[true, 0] = 1.fdiv(Math.sqrt(@params[:n_neighbors]))

  n_samples.times do |n|
    x_local = x[neighbor_ids[n, true], true]
    x_tangent[true, 1...] = right_singular_vectors(x_local, @params[:n_components])
    weight_mat = x_tangent.dot(x_tangent.transpose)
    neighbor_ids[n, true].each_with_index do |m, i|
      affinity_mat[m, neighbor_ids[n, true]] -= weight_mat[i, true]
      affinity_mat[m, m] += 1
    end
  end

  kernel_mat = 0.5 * (affinity_mat.transpose + affinity_mat)
  _, eig_vecs = Numo::Linalg.eigh(kernel_mat, vals_range: 1...(1 + @params[:n_components]))

  @embedding = @params[:n_components] == 1 ? eig_vecs[true, 0].dup : eig_vecs.dup
  @x_train = x.dup

  self
end

#fit_transform(x) ⇒ Numo::DFloat

Fit the model with training data, and then transform them with the learned model.

Returns (shape: [n_samples, n_components]) The transformed data.

Parameters:

  • x (Numo::DFloat)

    (shape: [n_samples, n_features]) The training data to be used for fitting the model.

Returns:

  • (Numo::DFloat)

    (shape: [n_samples, n_components]) The transformed data



86
87
88
89
90
91
92
# File 'rumale-manifold/lib/rumale/manifold/local_tangent_space_alignment.rb', line 86

def fit_transform(x, _y = nil)
  unless enable_linalg?(warning: false)
    raise 'LocalTangentSpaceAlignment#fit_transform requires Numo::Linalg but that is not loaded'
  end

  fit(x).transform(x)
end

#transform(x) ⇒ Numo::DFloat

Transform the given data with the learned model. For out-of-sample data embedding, the same method as Locally Linear Embedding is used.

Parameters:

  • x (Numo::DFloat)

    (shape: [n_samples, n_features]) The data to be transformed with the learned model.

Returns:

  • (Numo::DFloat)

    (shape: [n_samples, n_components]) The transformed data.



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'rumale-manifold/lib/rumale/manifold/local_tangent_space_alignment.rb', line 99

def transform(x)
  x = Rumale::Validation.check_convert_sample_array(x)

  n_samples = x.shape[0]
  tol = @params[:reg_param].fdiv(@params[:n_neighbors])
  distance_mat = Rumale::PairwiseMetric.squared_error(x, @x_train)
  neighbor_ids = neighbor_ids(distance_mat, @params[:n_neighbors], false)
  weight_mat = Numo::DFloat.zeros(n_samples, @x_train.shape[0])

  n_samples.times do |n|
    x_local = @x_train[neighbor_ids[n, true], true] - x[n, true]
    gram_mat = x_local.dot(x_local.transpose)
    gram_mat += tol * weight_mat.trace * Numo::DFloat.eye(@params[:n_neighbors])
    weights = Numo::Linalg.solve(gram_mat, Numo::DFloat.ones(@params[:n_neighbors]))
    weights /= weights.sum + 1e-8
    weight_mat[n, neighbor_ids[n, true]] = weights
  end

  weight_mat.dot(@embedding)
end