rumale-decomposition 0.28.1 → 0.29.0

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: aeae1f02b5c8e25473327db4cacc3c60e68fe430d3908a1c63c5225d11550979
4
- data.tar.gz: be0180f9bd0596bfa84b4f14621a1793badae6f290abc8216677fe923aa478cd
3
+ metadata.gz: 84715613fb41ef3b6961b9280f6750fc831daa161ddee4e3666ab906e0670599
4
+ data.tar.gz: c3079e09e5ca5b011d8067457eb89a451b8fd0819a8f996630018a1b4cac41e7
5
5
  SHA512:
6
- metadata.gz: ea0aa55f3f1980b70e81394a80da289c39d26a4d688b27af8ffbb4f0edf03a3e278be33312eae8262a6dbdac60800c98f54ccf4901dde175288b33defe794e3c
7
- data.tar.gz: b1882c08b9a13018dc7de6d56af43720bc06199cfaf530543d3d5e3d37aa20bb1e5843dc605589167d16593fdaf2863791d7c560d45d2940cdc044863e2e145d
6
+ metadata.gz: 8568f69c7acdf8ccc2c14ba6c9281e1ba145bcd400bf80ef9cbbdedd12f337455614170b1c5eff94c45ae8453ba00fb515deaad4a1ca0a56576a023264006f31
7
+ data.tar.gz: de013d05ad22b323d03e3eecac12edff941c3ecf9ad02e21a2302ee75cd6574cd06ccb1612d15bf758315191960065098212f70f8ea6980a26417dee3ef6090d
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2022-2023 Atsushi Tatsuma
1
+ Copyright (c) 2022-2024 Atsushi Tatsuma
2
2
  All rights reserved.
3
3
 
4
4
  Redistribution and use in source and binary forms, with or without
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rumale/base/estimator'
4
+ require 'rumale/base/transformer'
5
+ require 'rumale/utils'
6
+ require 'rumale/validation'
7
+
8
+ module Rumale
9
+ module Decomposition
10
+ # SparsePCA is a class that implements Sparse Principal Component Analysis.
11
+ #
12
+ # @example
13
+ # require 'numo/tiny_linalg'
14
+ # Numo::Linalg = Numo::TinyLinalg
15
+ #
16
+ # require 'rumale/decomposition/sparse_pca'
17
+ #
18
+ # decomposer = Rumale::Decomposition::SparsePCA.new(n_components: 2, reg_param: 0.1)
19
+ # representaion = decomposer.fit_transform(samples)
20
+ # sparse_components = decomposer.components
21
+ #
22
+ # *Reference*
23
+ # - Macky, L., "Deflation Methods for Sparse PCA," Advances in NIPS'08, pp. 1017--1024, 2008.
24
+ # - Hein, M. and Bühler, T., "An Inverse Power Method for Nonlinear Eigenproblems with Applications in 1-Spectral Clustering and Sparse PCA," Advances in NIPS'10, pp. 847--855, 2010.
25
+ class SparsePCA < ::Rumale::Base::Estimator
26
+ include ::Rumale::Base::Transformer
27
+
28
+ # Returns the principal components.
29
+ # @return [Numo::DFloat] (shape: [n_components, n_features])
30
+ attr_reader :components
31
+
32
+ # Returns the mean vector.
33
+ # @return [Numo::DFloat] (shape: [n_features])
34
+ attr_reader :mean
35
+
36
+ # Return the random generator.
37
+ # @return [Random]
38
+ attr_reader :rng
39
+
40
+ # Create a new transformer with Sparse PCA.
41
+ #
42
+ # @param n_components [Integer] The number of principal components.
43
+ # @param reg_param [Float] The regularization parameter (interval: [0, 1]).
44
+ # @param max_iter [Integer] The maximum number of iterations.
45
+ # @param tol [Float] The tolerance of termination criterion.
46
+ # @param random_seed [Integer] The seed value using to initialize the random generator.
47
+ def initialize(n_components: 2, reg_param: 0.001, max_iter: 1000, tol: 1e-6, random_seed: nil)
48
+ super()
49
+
50
+ warn('reg_param should be in the interval [0, 1].') unless (0..1).cover?(reg_param)
51
+
52
+ @params = {
53
+ n_components: n_components,
54
+ reg_param: reg_param,
55
+ max_iter: max_iter,
56
+ tol: tol,
57
+ random_seed: random_seed || srand
58
+ }
59
+ @rng = Random.new(@params[:random_seed])
60
+ end
61
+
62
+ # Fit the model with given training data.
63
+ #
64
+ # @overload fit(x) -> SparsePCA
65
+ # @param x [Numo::DFloat] (shape: [n_samples, n_features]) The training data to be used for fitting the model.
66
+ # @return [SparsePCA] The learned transformer itself.
67
+ def fit(x, _y = nil)
68
+ x = ::Rumale::Validation.check_convert_sample_array(x)
69
+
70
+ # initialize some variables.
71
+ @components = Numo::DFloat.zeros(@params[:n_components], x.shape[1])
72
+
73
+ # centering.
74
+ @mean = x.mean(axis: 0)
75
+ centered_x = x - @mean
76
+
77
+ # optimization.
78
+ partial_fit(centered_x)
79
+
80
+ @components = @components[0, true].dup if @params[:n_components] == 1
81
+
82
+ self
83
+ end
84
+
85
+ # Fit the model with training data, and then transform them with the learned model.
86
+ #
87
+ # @overload fit_transform(x) -> Numo::DFloat
88
+ # @param x [Numo::DFloat] (shape: [n_samples, n_features]) The training data to be used for fitting the model.
89
+ # @return [Numo::DFloat] (shape: [n_samples, n_components]) The transformed data
90
+ def fit_transform(x, _y = nil)
91
+ x = ::Rumale::Validation.check_convert_sample_array(x)
92
+
93
+ fit(x).transform(x)
94
+ end
95
+
96
+ # Transform the given data with the learned model.
97
+ #
98
+ # @param x [Numo::DFloat] (shape: [n_samples, n_features]) The data to be transformed with the learned model.
99
+ # @return [Numo::DFloat] (shape: [n_samples, n_components]) The transformed data.
100
+ def transform(x)
101
+ x = ::Rumale::Validation.check_convert_sample_array(x)
102
+
103
+ (x - @mean).dot(@components.transpose)
104
+ end
105
+
106
+ private
107
+
108
+ def partial_fit(x)
109
+ sub_rng = @rng.dup
110
+ n_samples, n_features = x.shape
111
+ cov_mat = x.transpose.dot(x) / n_samples
112
+ prj_mat = Numo::DFloat.eye(n_features)
113
+ @params[:n_components].times do |i|
114
+ f = ::Rumale::Utils.rand_normal(n_features, sub_rng)
115
+ xf = x.dot(f)
116
+ norm_xf = norm(xf, 2)
117
+ coeff = coeff_numerator(f).fdiv(norm_xf)
118
+ mu = cov_mat.dot(f) / norm_xf
119
+ @params[:max_iter].times do |_t|
120
+ g = sign(mu) * Numo::DFloat.maximum(coeff * mu.abs - @params[:reg_param], 0)
121
+ f = g / norm(x.dot(g), 2)
122
+ mu = cov_mat.dot(f) / norm(x.dot(f), 2)
123
+ coeff_new = coeff_numerator(f)
124
+
125
+ break if (coeff - coeff_new).abs.fdiv(coeff) < @params[:tol]
126
+
127
+ coeff = coeff_new
128
+ end
129
+
130
+ # deflation
131
+ q = prj_mat.dot(f)
132
+ qqt = Numo::DFloat.eye(n_features) - q.outer(q)
133
+ x = x.dot(qqt)
134
+ cov_mat = qqt.dot(cov_mat).dot(qqt)
135
+ prj_mat = prj_mat.dot(qqt)
136
+ f /= norm(f, 2)
137
+
138
+ @components[i, true] = f.dup
139
+ end
140
+ end
141
+
142
+ def coeff_numerator(f)
143
+ (1 - @params[:reg_param]) * norm(f, 2) + @params[:reg_param] * norm(f, 1)
144
+ end
145
+
146
+ def sign(v)
147
+ r = Numo::DFloat.zeros(v.size)
148
+ r[v.lt(0)] = -1
149
+ r[v.gt(0)] = 1
150
+ r
151
+ end
152
+
153
+ def norm(v, ord)
154
+ nrm = if defined?(Numo::Linalg)
155
+ Numo::Linalg.norm(v, ord)
156
+ elsif ord == 2
157
+ Math.sqrt(v.dot(v))
158
+ else
159
+ v.abs.sum
160
+ end
161
+ nrm.zero? ? 1.0 : nrm
162
+ end
163
+ end
164
+ end
165
+ end
@@ -5,6 +5,6 @@ module Rumale
5
5
  # Module for matrix decomposition algorithms.
6
6
  module Decomposition
7
7
  # @!visibility private
8
- VERSION = '0.28.1'
8
+ VERSION = '0.29.0'
9
9
  end
10
10
  end
@@ -6,4 +6,5 @@ require_relative 'decomposition/factor_analysis'
6
6
  require_relative 'decomposition/fast_ica'
7
7
  require_relative 'decomposition/nmf'
8
8
  require_relative 'decomposition/pca'
9
+ require_relative 'decomposition/sparse_pca'
9
10
  require_relative 'decomposition/version'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rumale-decomposition
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.28.1
4
+ version: 0.29.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - yoshoku
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-12-24 00:00:00.000000000 Z
11
+ date: 2024-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: numo-narray
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.28.1
33
+ version: 0.29.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.28.1
40
+ version: 0.29.0
41
41
  description: |
42
42
  Rumale::Decomposition provides matrix decomposition algorithms,
43
43
  such as Principal Component Analysis, Non-negative Matrix Factorization, Factor Analysis, and Independent Component Analysis,
@@ -55,6 +55,7 @@ files:
55
55
  - lib/rumale/decomposition/fast_ica.rb
56
56
  - lib/rumale/decomposition/nmf.rb
57
57
  - lib/rumale/decomposition/pca.rb
58
+ - lib/rumale/decomposition/sparse_pca.rb
58
59
  - lib/rumale/decomposition/version.rb
59
60
  homepage: https://github.com/yoshoku/rumale
60
61
  licenses:
@@ -80,7 +81,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
80
81
  - !ruby/object:Gem::Version
81
82
  version: '0'
82
83
  requirements: []
83
- rubygems_version: 3.4.22
84
+ rubygems_version: 3.5.7
84
85
  signing_key:
85
86
  specification_version: 4
86
87
  summary: Rumale::Decomposition provides matrix decomposition algorithms with Rumale