-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathDecomposition.fs
More file actions
93 lines (76 loc) · 3.8 KB
/
Copy pathDecomposition.fs
File metadata and controls
93 lines (76 loc) · 3.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
namespace Tensor.Algorithm
open Tensor
open Tensor.Utils
/// Matrix decomposition functions.
module Decomposition =
/// Information about a performed PCA or ZCA.
type PCAInfo<'T> = {
/// the means of the features (for centering the data)
Means: Tensor<'T>
/// the variances of the principal components in descending order
Variances: Tensor<'T>
/// the axes corresponding to the principal components
Axes: Tensor<'T>
}
/// Principal Component Analysis (PCA)
type PCA() =
/// Apply Principal Component Analysis (PCA) whitening.
/// `data` must be of the form [sample, feature].
/// `nComps` optionally specifies how many components to keep.
/// Returns a tensor of the form [sample, component].
static member Perform (data: Tensor<'T>, ?nComps) =
if data.NDims <> 2 then
invalidArg "data" "data must be a matrix"
let nFeatures = data.Shape.[1]
let nComps = defaultArg nComps nFeatures
if not (0L < nComps && nComps <= nFeatures) then
invalidArg "nComps" "nComps must be between 0 and the number of features"
// center data
let means = data |> Tensor.meanAxis 0
let centered = data - means.[NewAxis, *] // centered[smpl, feature]
// compute covariance matrix and its eigen decomposition
let n = HostTensor.scalar (conv<'T> data.Shape.[0])
let cov = (Tensor.transpose centered .* centered) / n
let variances, axes = Tensor.symmetricEigenDecomposition MatrixPart.Upper cov
// sort axes by their variances in descending order
let sortIdx =
variances
|> HostTensor.toList
|> List.indexed
|> List.sortByDescending snd
|> List.map fst
|> List.map int64
|> HostTensor.ofList
let variances = variances |> Tensor.gather [Some sortIdx]
let axesIdx = Tensor.replicate 0 axes.Shape.[0] sortIdx.[NewAxis, *]
let axes = axes |> Tensor.gather [None; Some axesIdx]
// limit number of components if desired
let variances = variances.[0L .. nComps-1L]
let axes = axes.[*, 0L .. nComps-1L]
// transform data into new coordinate system
// [smpl, feature] .* [feature, comp]
let pcaed = centered .* axes
// scale axes so that each has unit variance
let whitened = pcaed / sqrt variances.[NewAxis, *]
whitened, {Means=means; Variances=variances; Axes=axes}
/// Reverses PCA whitening.
/// `whitened` must be of the form [sample, component].
static member Reverse (whitened: Tensor<'T>,
{Means=means; Variances=variances; Axes=axes}) =
if whitened.NDims <> 2 then
invalidArg "whitened" "whitened must be a matrix"
let pcaed = whitened * sqrt variances.[NewAxis, *]
let centered = pcaed .* Tensor.transpose axes // [smpl, comp] .* [comp, feature]
centered + means.[NewAxis, *]
/// ZCA whitening
type ZCA() =
/// Apply ZCA whitening.
/// `data` must be of the form [sample, feature].
/// Returns a tensor of the form [sample, component].
static member Perform (data: Tensor<'T>) =
let whitened, info = PCA.Perform data
whitened .* Tensor.transpose info.Axes, info
/// Reverses ZCA whitening.
/// `whitened` must be of the form [sample, component].
static member Reverse (zcaed: Tensor<'T>, info: PCAInfo<'T>) =
PCA.Reverse (zcaed .* info.Axes, info)