rumale-decomposition 0.28.0 → 0.29.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/lib/rumale/decomposition/fast_ica.rb +1 -1
- data/lib/rumale/decomposition/nmf.rb +1 -1
- data/lib/rumale/decomposition/pca.rb +1 -1
- data/lib/rumale/decomposition/sparse_pca.rb +165 -0
- data/lib/rumale/decomposition/version.rb +1 -1
- data/lib/rumale/decomposition.rb +1 -0
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 84715613fb41ef3b6961b9280f6750fc831daa161ddee4e3666ab906e0670599
|
4
|
+
data.tar.gz: c3079e09e5ca5b011d8067457eb89a451b8fd0819a8f996630018a1b4cac41e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8568f69c7acdf8ccc2c14ba6c9281e1ba145bcd400bf80ef9cbbdedd12f337455614170b1c5eff94c45ae8453ba00fb515deaad4a1ca0a56576a023264006f31
|
7
|
+
data.tar.gz: de013d05ad22b323d03e3eecac12edff941c3ecf9ad02e21a2302ee75cd6574cd06ccb1612d15bf758315191960065098212f70f8ea6980a26417dee3ef6090d
|
data/LICENSE.txt
CHANGED
@@ -62,7 +62,7 @@ module Rumale
|
|
62
62
|
solver: 'fpt',
|
63
63
|
max_iter: max_iter,
|
64
64
|
tol: tol,
|
65
|
-
random_seed:
|
65
|
+
random_seed: random_seed || srand
|
66
66
|
}
|
67
67
|
@params[:solver] = 'evd' if (solver == 'auto' && enable_linalg?(warning: false)) || solver == 'evd'
|
68
68
|
@rng = Random.new(@params[:random_seed])
|
@@ -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
|
data/lib/rumale/decomposition.rb
CHANGED
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.
|
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:
|
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.
|
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.
|
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.
|
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
|