-
Notifications
You must be signed in to change notification settings - Fork 0
/
utilities.py
160 lines (126 loc) · 5.13 KB
/
utilities.py
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
"""
Utilites for feature visualisation
Author: Simon Thomas
Email: simon.thomas@uq.edu.au
Date: 5th July 2019
"""
import numpy as np
import sklearn.decomposition
def deprocess_image(x):
"""
Converts the (-inf, inf) image to have range 0-255.
Input:
x - image as numpy array
Output:
x - image a numpy array
"""
x -= x.mean()
x /= (x.std() + 1e-5)
x *= 0.1
x += 0.5 # Clips to [0, 1]
x = np.clip(x, 0, 1)
x *= 255
x = np.clip(x, 0, 255).astype('uint8') # Converts to RGB array
return x
"""Helper for using sklearn.decomposition on high-dimensional tensors.
Provides ChannelReducer, a wrapper around sklearn.decomposition to help them
apply to arbitrary rank tensors. It saves lots of annoying reshaping.
"""
class ChannelReducer(object):
"""Helper for dimensionality reduction to the innermost dimension of a tensor.
This class wraps sklearn.decomposition classes to help them apply to arbitrary
rank tensors. It saves lots of annoying reshaping.
See the original sklearn.decomposition documentation:
http://scikit-learn.org/stable/modules/classes.html#module-sklearn.decomposition
"""
def __init__(self, n_components=3, reduction_alg="NMF", **kwargs):
"""Constructor for ChannelReducer.
Inputs:
n_components: Numer of dimensions to reduce inner most dimension to.
reduction_alg: A string or sklearn.decomposition class. Defaults to
"NMF" (non-negative matrix facotrization). Other options include:
"PCA", "FastICA", and "MiniBatchDictionaryLearning". The name of any of
the sklearn.decomposition classes will work, though.
kwargs: Additional kwargs to be passed on to the reducer.
"""
if not isinstance(n_components, int):
raise ValueError("n_components must be an int, not '%s'." % n_components)
# Defensively look up reduction_alg if it is a string and give useful errors.
algorithm_map = {}
for name in dir(sklearn.decomposition):
obj = sklearn.decomposition.__getattribute__(name)
if isinstance(obj, type) and issubclass(obj, sklearn.decomposition.base.BaseEstimator):
algorithm_map[name] = obj
if isinstance(reduction_alg, str):
if reduction_alg in algorithm_map:
reduction_alg = algorithm_map[reduction_alg]
else:
raise ValueError("Unknown dimensionality reduction method '%s'." % reduction_alg)
self.n_components = n_components
self._reducer = reduction_alg(n_components=n_components, **kwargs)
self._is_fit = False
@classmethod
def _apply_flat(cls, f, acts):
"""Utility for applying f to inner dimension of acts.
Flattens acts into a 2D tensor, applies f, then unflattens so that all
dimesnions except innermost are unchanged.
"""
orig_shape = acts.shape
acts_flat = acts.reshape([-1, acts.shape[-1]])
new_flat = f(acts_flat)
if not isinstance(new_flat, np.ndarray):
return new_flat
shape = list(orig_shape[:-1]) + [-1]
return new_flat.reshape(shape)
def fit(self, acts):
self._is_fit = True
return ChannelReducer._apply_flat(self._reducer.fit, acts)
def fit_transform(self, acts):
self._is_fit = True
return ChannelReducer._apply_flat(self._reducer.fit_transform, acts)
def transform(self, acts):
return ChannelReducer._apply_flat(self._reducer.transform, acts)
def __call__(self, acts):
if self._is_fit:
return self.transform(acts)
else:
return self.fit_transform(acts)
def __getattr__(self, name):
if name in self.__dict__:
return self.__dict__[name]
elif name + "_" in self._reducer.__dict__:
return self._reducer.__dict__[name+"_"]
def __dir__(self):
dynamic_attrs = [name[:-1]
for name in dir(self._reducer)
if name[-1] == "_" and name[0] != "_"
]
return list(ChannelReducer.__dict__.keys()) + list(self.__dict__.keys()) + dynamic_attrs
def rfft2d_freqs(h, w):
"""Computes 2D spectrum frequencies."""
fy = np.fft.fftfreq(h)[:, None]
# when we have an odd input dimension we need to keep one additional
# frequency and later cut off 1 pixel
if w % 2 == 1:
fx = np.fft.fftfreq(w)[:]
else:
fx = np.fft.fftfreq(w)[:]
return np.sqrt(fx * fx + fy * fy)
def fft_image(shape, sd=None, decay_power=1):
"""An image paramaterization using 2D Fourier coefficients."""
sd = sd or 0.01
batch, h, w, ch = shape
freqs = rfft2d_freqs(h, w)
init_val_size = (2, ch) + freqs.shape
# Randomise the values
init_val = np.random.normal(size=init_val_size, scale=sd).astype(np.float32)
# Compose of real and imaginary parts
spectrum_t = init_val[0] + init_val[1]*1j
# Scale the spectrum. First normalize energy, then scale by the square-root
# of the number of pixels to get a unitary transformation.
# This allows to use similar leanring rates to pixel-wise optimisation.
scale = 1.0 / np.maximum(freqs, 1.0 / max(w, h)) ** decay_power
scale *= np.sqrt(w * h)
scaled_spectrum_t = scale * spectrum_t
image_t = np.fft.ifft2(np.transpose(scaled_spectrum_t, (1, 2, 0))).real
return image_t # Magic constant