danlin1128 commited on
Commit
67069a4
·
verified ·
1 Parent(s): daa533e

Upload 33 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,11 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ denoising_example_16.gif filter=lfs diff=lfs merge=lfs -text
37
+ denoising_example_25.gif filter=lfs diff=lfs merge=lfs -text
38
+ denoising_example_4.gif filter=lfs diff=lfs merge=lfs -text
39
+ denoising_example_9.gif filter=lfs diff=lfs merge=lfs -text
40
+ noising_example_16.gif filter=lfs diff=lfs merge=lfs -text
41
+ noising_example_25.gif filter=lfs diff=lfs merge=lfs -text
42
+ noising_example_4.gif filter=lfs diff=lfs merge=lfs -text
43
+ noising_example_9.gif filter=lfs diff=lfs merge=lfs -text
GAN/.DS_Store ADDED
Binary file (6.15 kB). View file
 
GAN/__pycache__/dataset.cpython-38.pyc ADDED
Binary file (5.91 kB). View file
 
GAN/__pycache__/diffusion.cpython-311.pyc ADDED
Binary file (52 kB). View file
 
GAN/__pycache__/gan.cpython-38.pyc ADDED
Binary file (4.97 kB). View file
 
GAN/__pycache__/timegan.cpython-38.pyc ADDED
Binary file (14.9 kB). View file
 
GAN/__pycache__/timevae.cpython-38.pyc ADDED
Binary file (14.7 kB). View file
 
GAN/__pycache__/utils.cpython-311.pyc ADDED
Binary file (18.1 kB). View file
 
GAN/__pycache__/utils.cpython-38.pyc ADDED
Binary file (8.15 kB). View file
 
GAN/__pycache__/vae.cpython-38.pyc ADDED
Binary file (3.93 kB). View file
 
GAN/__pycache__/zoo.cpython-38.pyc ADDED
Binary file (13.9 kB). View file
 
GAN/diffusion.py ADDED
@@ -0,0 +1,965 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ import shutil
3
+ import numpy as np
4
+ from tqdm.auto import tqdm
5
+ import tensorflow as tf
6
+ from tensorflow import keras
7
+ from tensorflow.keras import layers
8
+ from GAN.utils import linear_beta_schedule, cosine_beta_schedule
9
+ import matplotlib.pyplot as plt
10
+ import os
11
+ from GAN.utils import TSFeatureScaler
12
+
13
+ class GaussianDiffusion:
14
+
15
+ def __init__(
16
+ self,
17
+ beta_schedule='cosine',
18
+ timesteps=10,
19
+ clip_min=-1.0,
20
+ clip_max=1.0,
21
+ ):
22
+
23
+ self.timesteps = timesteps
24
+ self.clip_min = clip_min
25
+ self.clip_max = clip_max
26
+
27
+ if beta_schedule == 'linear':
28
+ betas = linear_beta_schedule(timesteps)
29
+ elif beta_schedule == 'cosine':
30
+ betas = cosine_beta_schedule(timesteps)
31
+ else:
32
+ raise ValueError(f'unknown beta schedule {beta_schedule}')
33
+
34
+ alphas = 1. - betas
35
+ alphas_cumprod = np.cumprod(alphas, axis=0)
36
+ alphas_cumprod_prev = np.append(1., alphas_cumprod[:-1])
37
+
38
+ self.betas = tf.constant(betas, dtype=tf.float32)
39
+ self.alphas_cumprod = tf.constant(alphas_cumprod, dtype=tf.float32)
40
+ self.alphas_cumprod_prev = tf.constant(alphas_cumprod_prev, dtype=tf.float32)
41
+ self.sqrt_recip_alphas = tf.constant(np.sqrt(1. / alphas), dtype=tf.float32)
42
+
43
+ self.sqrt_alphas_cumprod = tf.constant(np.sqrt(self.alphas_cumprod), dtype=tf.float32)
44
+ self.sqrt_one_minus_alphas_cumprod = tf.constant(np.sqrt(1.0 - self.alphas_cumprod), dtype=tf.float32)
45
+ self.log_one_minus_alphas_cumprod = tf.constant(np.log(1. - alphas_cumprod), dtype=tf.float32)
46
+ self.sqrt_recip_alphas_cumprod = tf.constant(np.sqrt(1. / alphas_cumprod), dtype=tf.float32)
47
+ self.sqrt_recipm1_alphas_cumprod = tf.constant(np.sqrt(1.0 / alphas_cumprod - 1), dtype=tf.float32)
48
+ self.posterior_variance = (betas * (1.0 - alphas_cumprod_prev) / (1.0 - alphas_cumprod))
49
+
50
+ self.posterior_log_variance_clipped = tf.constant(
51
+ np.log(np.maximum(self.posterior_variance, 1e-20)), dtype=tf.float32
52
+ )
53
+
54
+ self.posterior_mean_coef1 = tf.constant(
55
+ betas * np.sqrt(alphas_cumprod_prev) / (1.0 - alphas_cumprod),
56
+ dtype=tf.float32,
57
+ )
58
+
59
+ self.posterior_mean_coef2 = tf.constant(
60
+ (1.0 - alphas_cumprod_prev) * np.sqrt(alphas) / (1.0 - alphas_cumprod),
61
+ dtype=tf.float32,
62
+ )
63
+
64
+ def _extract(self, a, t, x_shape):
65
+
66
+ batch_size = x_shape[0]
67
+ out = tf.gather(a, t)
68
+ return tf.reshape(out, [batch_size, 1, 1])
69
+
70
+ def q_sample(self, x_start, t):
71
+
72
+ x_start_shape = tf.shape(x_start)
73
+ samp = self._extract(self.sqrt_alphas_cumprod, t, x_start_shape) * x_start
74
+ noise = tf.random.normal(shape=tf.shape(x_start), dtype='float32')
75
+ weight_noise = self._extract(self.sqrt_one_minus_alphas_cumprod, t, x_start_shape) * noise * 0.5
76
+ # diffused_sample = self._extract(self.sqrt_alphas_cumprod, t, x_start_shape) * x_start + self._extract(
77
+ # self.sqrt_one_minus_alphas_cumprod, t, x_start_shape) * noise #* 0.1
78
+ diffused_sample = x_start + weight_noise
79
+ diffused_sample = tf.clip_by_value(diffused_sample , -0.99, 0.99)
80
+ weight_noise = diffused_sample - x_start
81
+ return samp, weight_noise, diffused_sample
82
+
83
+ def predict_start_from_noise(self, x_t, t, noise):
84
+
85
+ x_t_shape = tf.shape(x_t)
86
+ return (
87
+ self._extract(self.sqrt_recip_alphas_cumprod, t, x_t_shape) * x_t
88
+ - self._extract(self.sqrt_recipm1_alphas_cumprod, t, x_t_shape) * noise
89
+ )
90
+
91
+ def q_posterior(self, x_start, x_t, t):
92
+
93
+ x_t_shape = tf.shape(x_t)
94
+ posterior_mean = (
95
+ self._extract(self.posterior_mean_coef1, t, x_t_shape) * x_start
96
+ + self._extract(self.posterior_mean_coef2, t, x_t_shape) * x_t
97
+ )
98
+ posterior_variance = self._extract(self.posterior_variance, t, x_t_shape)
99
+ posterior_log_variance_clipped = self._extract(
100
+ self.posterior_log_variance_clipped, t, x_t_shape
101
+ )
102
+ return posterior_mean, posterior_variance, posterior_log_variance_clipped
103
+
104
+ def p_mean_variance(self, pred_noise, x, t, clip_denoised=False):
105
+
106
+ x_recon = self.predict_start_from_noise(x, t=t, noise=pred_noise)
107
+
108
+ if clip_denoised:
109
+ x_recon = tf.clip_by_value(x_recon, self.clip_min, self.clip_max)
110
+
111
+ model_mean, posterior_variance, posterior_log_variance = self.q_posterior(
112
+ x_start=x_recon, x_t=x, t=t
113
+ )
114
+ return model_mean, posterior_variance, posterior_log_variance
115
+
116
+ def p_sample(self, pred_noise, x, t, clip_denoised=False):
117
+
118
+ model_mean, _, model_log_variance = self.p_mean_variance(
119
+ pred_noise, x=x, t=t, clip_denoised=clip_denoised
120
+ )
121
+ variance_term = tf.exp(0.5 * model_log_variance)
122
+ noise = tf.random.normal(shape=tf.shape(x), dtype=x.dtype)
123
+ nonzero_mask = tf.reshape(
124
+ 1 - tf.cast(tf.equal(t, 0), tf.float32), [tf.shape(x)[0], 1, 1]
125
+ )
126
+ noise_term = variance_term * nonzero_mask * noise
127
+ sample = model_mean + noise_term
128
+ return sample
129
+
130
+
131
+ class TimeEmbedding(layers.Layer):
132
+ def __init__(self, dim, **kwargs):
133
+ super().__init__(**kwargs)
134
+ self.dim = dim
135
+ self.half_dim = dim // 2
136
+ self.emb = math.log(10000) / (self.half_dim - 1)
137
+ self.emb = tf.exp(tf.range(self.half_dim, dtype=tf.float32) * -self.emb)
138
+
139
+ def call(self, inputs):
140
+ inputs = tf.cast(inputs, dtype=tf.float32)
141
+ emb = inputs[:, None] * self.emb[None, :]
142
+ emb = tf.concat([tf.sin(emb), tf.cos(emb)], axis=-1)
143
+ return emb
144
+
145
+ def TimeMLP(units, activation_fn=keras.activations.swish):
146
+ def apply(inputs):
147
+ temb = layers.Dense(
148
+ units, activation=activation_fn, kernel_initializer=kernel_init(1.0)
149
+ )(inputs)
150
+ # temb = layers.Dense(units, kernel_initializer=kernel_init(1.0))(temb)
151
+ return temb
152
+
153
+ return apply
154
+
155
+ # Kernel initializer to use
156
+ def kernel_init(scale):
157
+ scale = max(scale, 1e-10)
158
+ return keras.initializers.VarianceScaling(
159
+ scale, mode="fan_avg", distribution="uniform"
160
+ )
161
+
162
+ def build_encoder_time(embed_dim=16, num_heads=2, ff_dim=32):
163
+ def apply(inputs):
164
+ x, t = inputs
165
+ position_embedding_layer = layers.Embedding(x.shape[1], embed_dim)
166
+ pos_encoding = position_embedding_layer(tf.range(x.shape[1]))
167
+ embeddings = x + pos_encoding + t
168
+
169
+ # Encoder blocks
170
+ for _ in range(2): # Repeat twice
171
+ # Multi-head self-attention mechanism
172
+ attention_output, attention_score = layers.MultiHeadAttention(num_heads=num_heads, key_dim=embed_dim)(
173
+ embeddings, embeddings, return_attention_scores=True)
174
+
175
+ # Add residual connection and layer normalization
176
+ x = layers.Add()([embeddings, attention_output])
177
+ x = layers.LayerNormalization(epsilon=1e-6)(x)
178
+
179
+ # Feed-forward network
180
+ ff_output = layers.Dense(ff_dim, activation="relu")(x)
181
+ ff_output = layers.Dense(embed_dim)(ff_output)
182
+
183
+ # Add residual connection and layer normalization
184
+ x = layers.Add()([x, ff_output])
185
+ x = layers.LayerNormalization(epsilon=1e-6)(x)
186
+
187
+ return x, attention_score
188
+
189
+ return apply
190
+
191
+ def build_encoder_variales(embed_dim=16, num_heads=2, ff_dim=32):
192
+ def apply(inputs):
193
+ x, t = inputs
194
+ x = layers.Conv1D(16, kernel_size=3, padding='same')(x)
195
+ embeddings = x + t
196
+
197
+ # Encoder blocks
198
+ for _ in range(2): # Repeat twice
199
+ # Multi-head self-attention mechanism
200
+ attention_output, attention_score = layers.MultiHeadAttention(num_heads=num_heads, key_dim=embed_dim)(
201
+ embeddings, embeddings, return_attention_scores=True)
202
+
203
+ # Add residual connection and layer normalization
204
+ x = layers.Add()([embeddings, attention_output])
205
+ x = layers.LayerNormalization(epsilon=1e-6)(x)
206
+
207
+ # Feed-forward network
208
+ ff_output = layers.Dense(ff_dim, activation="relu")(x)
209
+ ff_output = layers.Dense(embed_dim)(ff_output)
210
+
211
+ # Add residual connection and layer normalization
212
+ x = layers.Add()([x, ff_output])
213
+ x = layers.LayerNormalization(epsilon=1e-6)(x)
214
+
215
+ return x, attention_score
216
+
217
+ return apply
218
+
219
+ def build_decoder(embed_dim=16, num_heads=2, ff_dim=32):
220
+ def apply(inputs):
221
+ encoder_outputs, t = inputs
222
+ position_embedding_layer = layers.Embedding(encoder_outputs.shape[1], embed_dim)
223
+ pos_encoding = position_embedding_layer(tf.range(encoder_outputs.shape[1]))
224
+ dec_embeddings = encoder_outputs + pos_encoding + t
225
+
226
+ # Decoder blocks
227
+ dec_output = dec_embeddings
228
+ for _ in range(2): # Repeat twice
229
+
230
+ # Multi-head attention over encoder outputs
231
+ attention2_output = layers.MultiHeadAttention(num_heads=num_heads, key_dim=embed_dim)(
232
+ dec_output, encoder_outputs)
233
+
234
+ # Add residual connection and layer normalization
235
+ dec_output = layers.Add()([dec_output, attention2_output])
236
+ dec_output = layers.LayerNormalization(epsilon=1e-6)(dec_output)
237
+
238
+ # Feed-forward network
239
+ ff_output = layers.Dense(ff_dim, activation="relu")(dec_output)
240
+ ff_output = layers.Dense(embed_dim)(ff_output)
241
+
242
+ # Add residual connection and layer normalization
243
+ dec_output = layers.Add()([dec_output, ff_output])
244
+ dec_output = layers.LayerNormalization(epsilon=1e-6)(dec_output)
245
+
246
+ return dec_output
247
+
248
+ return apply
249
+
250
+ def build_model(time_len, fea_num, d_model=16, n_heads=2, encoder_type='dual'):
251
+ """
252
+ Build the transformer-based diffusion model.
253
+ """
254
+ print(f"\nBuilding model with encoder type: {encoder_type}")
255
+ print(f"Input shape: time_len={time_len}, features={fea_num}, d_model={d_model}")
256
+
257
+ # Input layers
258
+ x_input = layers.Input(shape=(time_len, fea_num))
259
+ time_input = layers.Input(shape=())
260
+
261
+ # Time step embeddings
262
+ time_emb = get_time_embedding(time_input, d_model)
263
+
264
+ encoded_features = []
265
+
266
+ if encoder_type in ['time', 'dual']:
267
+ print("→ Using Time Transformer Encoder")
268
+ # Time Transformer
269
+ time_encoded = time_transformer_encoder(
270
+ x_input,
271
+ time_emb,
272
+ d_model=d_model,
273
+ n_heads=n_heads
274
+ )
275
+ print(f" Time encoder output shape: {time_encoded.shape}")
276
+ encoded_features.append(time_encoded)
277
+
278
+ if encoder_type in ['pairwise', 'dual']:
279
+ print("→ Using Pairwise Correlation Encoder")
280
+ # Pairwise Correlation Transformer
281
+ pairwise_encoded = pairwise_transformer_encoder(
282
+ x_input,
283
+ time_emb,
284
+ d_model=d_model,
285
+ n_heads=n_heads
286
+ )
287
+ print(f" Pairwise encoder output shape: {pairwise_encoded.shape}")
288
+ encoded_features.append(pairwise_encoded)
289
+
290
+ # Combine encodings based on encoder type
291
+ if encoder_type == 'dual':
292
+ print("→ Combining both encoders")
293
+ encoded = layers.Concatenate(axis=-1)(encoded_features)
294
+ print(f" Combined shape before projection: {encoded.shape}")
295
+ encoded = layers.Dense(d_model)(encoded)
296
+ print(f" Final encoded shape after projection: {encoded.shape}")
297
+ else:
298
+ encoded = encoded_features[0]
299
+ print(f"→ Using single encoder output shape: {encoded.shape}")
300
+
301
+ # Add residual connection
302
+ if encoder_type != 'dual':
303
+ print("→ Adding residual connection")
304
+ encoded = layers.Add()([encoded, layers.Dense(d_model)(x_input)])
305
+
306
+ # Decoder
307
+ decoded = decoder_module(encoded, time_emb)
308
+ print(f"→ Decoder output shape: {decoded.shape}")
309
+
310
+ # Final output layer
311
+ output = layers.Dense(fea_num)(decoded)
312
+ print(f"→ Final output shape: {output.shape}")
313
+ print("Model building completed!\n")
314
+
315
+ return keras.Model(inputs=[x_input, time_input], outputs=output)
316
+
317
+ def pairwise_transformer_encoder(x, time_emb, d_model=16, n_heads=2):
318
+ """Pairwise Correlation Transformer encoder implementation"""
319
+ # Get input shape
320
+ input_shape = x.shape
321
+ time_len = input_shape[1]
322
+
323
+ # Transpose input to treat features as sequence
324
+ x_transposed = tf.transpose(x, perm=[0, 2, 1]) # [batch, features, time]
325
+
326
+ # Project input
327
+ x_proj = layers.Dense(d_model)(x_transposed)
328
+
329
+ # Expand time embeddings
330
+ time_emb = tf.expand_dims(time_emb, 1)
331
+ time_emb = tf.tile(time_emb, [1, tf.shape(x_proj)[1], 1])
332
+
333
+ # Add time embeddings
334
+ x = x_proj + time_emb
335
+
336
+ # Transformer encoder layers
337
+ for _ in range(2):
338
+ x = transformer_encoder_layer(x, d_model, n_heads)
339
+
340
+ # Project to correct time dimension and transpose back
341
+ x = layers.Dense(time_len)(x) # Project to original time dimension
342
+ x = tf.transpose(x, perm=[0, 2, 1]) # [batch, time, features]
343
+
344
+ # Final projection to match d_model dimension
345
+ x = layers.Dense(d_model)(x) # [batch, time_len, d_model]
346
+
347
+ return x
348
+
349
+ def time_transformer_encoder(x, time_emb, d_model=16, n_heads=2):
350
+ """Time Transformer encoder implementation"""
351
+ # Position embeddings
352
+ pos_emb = get_positional_embedding(tf.shape(x)[1], d_model)
353
+
354
+ # Project input
355
+ x = layers.Dense(d_model)(x)
356
+
357
+ # Expand time embeddings
358
+ time_emb = tf.expand_dims(time_emb, 1)
359
+ time_emb = tf.tile(time_emb, [1, tf.shape(x)[1], 1])
360
+
361
+ # Add embeddings
362
+ x = x + pos_emb + time_emb
363
+
364
+ # Transformer encoder layers
365
+ for _ in range(2):
366
+ x = transformer_encoder_layer(x, d_model, n_heads)
367
+
368
+ return x # [batch, time_len, d_model]
369
+
370
+ def transformer_encoder_layer(x, d_model, n_heads):
371
+ """Single transformer encoder layer with multi-head attention"""
372
+ # Multi-head self attention
373
+ attention_output = layers.MultiHeadAttention(
374
+ num_heads=n_heads,
375
+ key_dim=d_model // n_heads
376
+ )(x, x)
377
+
378
+ # Add & Norm
379
+ x = layers.Add()([x, attention_output])
380
+ x = layers.LayerNormalization()(x)
381
+
382
+ # Feed Forward Network
383
+ ffn_output = layers.Dense(d_model * 4, activation='relu')(x)
384
+ ffn_output = layers.Dense(d_model)(ffn_output)
385
+
386
+ # Add & Norm
387
+ x = layers.Add()([x, ffn_output])
388
+ x = layers.LayerNormalization()(x)
389
+
390
+ return x
391
+
392
+ def decoder_module(encoded, time_emb):
393
+ """Decoder implementation"""
394
+ # Expand time embeddings
395
+ time_emb = tf.expand_dims(time_emb, 1)
396
+ time_emb = tf.tile(time_emb, [1, tf.shape(encoded)[1], 1])
397
+
398
+ # Concatenate along feature dimension
399
+ x = layers.Concatenate(axis=-1)([encoded, time_emb])
400
+
401
+ # Decoder layers
402
+ x = layers.Dense(256, activation='relu')(x)
403
+ x = layers.Dense(128, activation='relu')(x)
404
+
405
+ return x
406
+
407
+ def get_time_embedding(timesteps, embedding_dim):
408
+ """
409
+ Create sinusoidal time embeddings.
410
+
411
+ Args:
412
+ timesteps: Tensor of shape [batch_size] containing timesteps
413
+ embedding_dim: Dimension of the embeddings to create
414
+ """
415
+ # Ensure timesteps is a 2D tensor
416
+ timesteps = tf.expand_dims(timesteps, -1)
417
+
418
+ # Calculate positions and dimensions
419
+ half_dim = embedding_dim // 2
420
+ emb = math.log(10000) / (half_dim - 1)
421
+ emb = tf.exp(tf.range(half_dim, dtype=tf.float32) * -emb)
422
+
423
+ # Create embeddings
424
+ emb = tf.cast(timesteps, dtype=tf.float32) * emb[None, :]
425
+ emb = tf.concat([tf.sin(emb), tf.cos(emb)], axis=-1)
426
+
427
+ # Handle odd embedding dimensions
428
+ if embedding_dim % 2 == 1:
429
+ emb = tf.pad(emb, [[0, 0], [0, 1]])
430
+
431
+ return emb # Shape: [batch_size, embedding_dim]
432
+
433
+ def get_positional_embedding(sequence_length, embedding_dim):
434
+ """
435
+ Create sinusoidal position embeddings.
436
+
437
+ Args:
438
+ sequence_length: Length of the sequence
439
+ embedding_dim: Dimension of the embeddings to create
440
+ """
441
+ # Create position indices
442
+ positions = tf.range(sequence_length, dtype=tf.float32)[:, tf.newaxis]
443
+
444
+ # Create dimension indices
445
+ dimensions = tf.range(0, embedding_dim, 2, dtype=tf.float32)[tf.newaxis, :]
446
+
447
+ # Calculate angle rates
448
+ angle_rates = 1 / tf.pow(10000.0, (2 * dimensions) / tf.cast(embedding_dim, tf.float32))
449
+
450
+ # Calculate angle rads
451
+ angle_rads = positions * angle_rates
452
+
453
+ # Apply sin and cos
454
+ pos_encoding = tf.concat(
455
+ [tf.sin(angle_rads), tf.cos(angle_rads)],
456
+ axis=-1
457
+ )
458
+
459
+ # Handle odd embedding dimensions
460
+ if embedding_dim % 2 == 1:
461
+ pos_encoding = tf.pad(pos_encoding, [[0, 0], [0, 1]])
462
+
463
+ # Add batch dimension
464
+ pos_encoding = tf.expand_dims(pos_encoding, 0)
465
+
466
+ return pos_encoding
467
+
468
+ class DiffusionModel(keras.Model):
469
+ def __init__(self, network, ema_network, timesteps, gdf_util, data, ema=0.999):
470
+ super().__init__()
471
+ self.network = network
472
+ self.ema_network = ema_network
473
+ self.timesteps = timesteps
474
+ self.gdf_util = gdf_util
475
+ self.data = data
476
+ self.ema = ema
477
+
478
+ def train_step(self, data):
479
+
480
+ batch_size = tf.shape(data)[0]
481
+
482
+ t = tf.random.uniform(
483
+ minval=0,
484
+ maxval=self.timesteps,
485
+ shape=(batch_size,),
486
+ dtype=tf.int32
487
+ )
488
+
489
+ old_weights = [tf.identity(w) for w in self.network.trainable_weights]
490
+
491
+ with tf.GradientTape() as tape:
492
+ _, noise, x_t = self.gdf_util.q_sample(data, t)
493
+ pred_noise = self.network([x_t, t], training=True)
494
+ loss = self.loss(noise, pred_noise)
495
+
496
+ gradients = tape.gradient(loss, self.network.trainable_weights)
497
+ self.optimizer.apply_gradients(zip(gradients, self.network.trainable_weights))
498
+
499
+ for weight, ema_weight in zip(self.network.weights, self.ema_network.weights):
500
+ ema_weight.assign(self.ema * ema_weight + (1 - self.ema) * weight)
501
+
502
+ new_weights = self.network.trainable_weights
503
+ weight_changes = []
504
+ for old_w, new_w in zip(old_weights, new_weights):
505
+ diff = tf.reduce_max(tf.abs(old_w - new_w))
506
+ weight_changes.append(diff)
507
+ max_change = tf.reduce_max(weight_changes)
508
+
509
+ return {
510
+ "loss": loss,
511
+ "weight_max_change": max_change,
512
+ "has_weight_changed": max_change > 0
513
+ }
514
+
515
+ def check_noise_levels(self):
516
+
517
+ x_0 = tf.cast(self.data, tf.float32)
518
+
519
+ print("\n=== Noise Level Analysis ===")
520
+ print(f"Using all {len(self.data)} samples")
521
+ print("Checking noise levels at different timesteps:")
522
+
523
+ timesteps_to_check = [
524
+ 0,
525
+ self.timesteps//4,
526
+ self.timesteps//2,
527
+ 3*self.timesteps//4,
528
+ self.timesteps-1
529
+ ]
530
+
531
+ for t in timesteps_to_check:
532
+
533
+ sqrt_alphas = tf.sqrt(self.gdf_util.alphas_cumprod[t])
534
+ sqrt_one_minus_alphas = tf.sqrt(1 - self.gdf_util.alphas_cumprod[t])
535
+
536
+ _, noise, x_t = self.gdf_util.q_sample(x_0, tf.fill([len(x_0)], t))
537
+ pred_noise = self.ema_network.predict([x_t, tf.fill([len(x_0)], t)], verbose=0)
538
+
539
+ print(f"\nTimestep {t}:")
540
+ print(f"Signal scaling factor (sqrt_alphas): {sqrt_alphas:.4f}")
541
+ print(f"Noise scaling factor (sqrt_1-alphas): {sqrt_one_minus_alphas:.4f}")
542
+ print(f"Original data range: [{tf.reduce_min(x_0):.4f}, {tf.reduce_max(x_0):.4f}]")
543
+ print(f"Noisy data range: [{tf.reduce_min(x_t):.4f}, {tf.reduce_max(x_t):.4f}]")
544
+ print(f"Added noise - Mean: {tf.reduce_mean(noise):.4f}, Std: {tf.math.reduce_std(noise):.4f}")
545
+ print(f"Scaled noise - Mean: {tf.reduce_mean(sqrt_one_minus_alphas * noise):.4f}, Std: {tf.math.reduce_std(sqrt_one_minus_alphas * noise):.4f}")
546
+ print(f"Predicted noise - Mean: {tf.reduce_mean(pred_noise):.4f}, Std: {tf.math.reduce_std(pred_noise):.4f}")
547
+ print(f"Noise prediction error: {tf.reduce_mean(tf.abs(noise - pred_noise)):.4f}")
548
+
549
+ def generate_ts(self, num_ts=16):
550
+
551
+ if num_ts > len(self.data):
552
+ indices = tf.random.uniform(
553
+ shape=[num_ts],
554
+ minval=0,
555
+ maxval=len(self.data),
556
+ dtype=tf.int32
557
+ )
558
+ initial_samples = tf.cast(
559
+ tf.gather(self.data, indices),
560
+ tf.float32
561
+ )
562
+ else:
563
+ initial_samples = self.data
564
+
565
+ _, _, samples = self.gdf_util.q_sample(initial_samples, tf.fill([num_ts], self.timesteps-1))
566
+ samples0 = samples
567
+
568
+ for i in reversed(range(0, self.timesteps)):
569
+ tt = tf.fill([num_ts], i)
570
+ pred_noise = self.ema_network.predict([samples0, tt], verbose=0, batch_size=num_ts
571
+ )
572
+ # print(f"\nStep {i}:")
573
+ # print(f"Predicted noise - Mean: {tf.reduce_mean(pred_noise):.4f}")
574
+ # print(f"Predicted noise - Std: {tf.math.reduce_std(pred_noise):.4f}")
575
+ # print(f"Predicted noise - Min: {tf.reduce_min(pred_noise):.4f}")
576
+ # print(f"Predicted noise - Max: {tf.reduce_max(pred_noise):.4f}")
577
+
578
+ samples = self.gdf_util.p_sample(
579
+ pred_noise, samples0, tt, clip_denoised=False
580
+ )
581
+ # scaler = TSFeatureScaler()
582
+ # print(f"Generated samples - Mean: {tf.reduce_mean(scaler.fit_transform(samples.numpy())):.4f}")
583
+ # print(f"Generated samples- Std: {tf.math.reduce_std(scaler.fit_transform(samples.numpy())):.4f}")
584
+ # print(f"Generated samples - Min: {tf.reduce_min(scaler.fit_transform(samples.numpy())):.4f}")
585
+ # print(f"Generated samples - Max: {tf.reduce_max(scaler.fit_transform(samples.numpy())):.4f}")
586
+
587
+ return samples
588
+
589
+ def plot_denoise_process(self, save_dir='denoise_process'):
590
+
591
+ if os.path.exists(save_dir):
592
+ shutil.rmtree(save_dir)
593
+ os.makedirs(save_dir, exist_ok=True)
594
+
595
+ indices = tf.random.uniform(
596
+ shape=[16],
597
+ minval=0,
598
+ maxval=len(self.data),
599
+ dtype=tf.int32
600
+ )
601
+ x_0 = tf.cast(tf.gather(self.data, indices), tf.float32)
602
+ _, _, samples = self.gdf_util.q_sample(x_0, tf.fill([16], self.timesteps-1))
603
+ samples0 = samples
604
+
605
+ time_steps = np.arange(self.data.shape[1])
606
+
607
+ self._plot_step_grid(x_0.numpy(), self.timesteps, time_steps, save_dir)
608
+ print(f"Denoising: Generate 16 samples for visualization")
609
+
610
+ for i in reversed(range(0, self.timesteps)):
611
+ print(f"Processing step {i}/{self.timesteps}")
612
+ tt = tf.fill([16], i)
613
+ pred_noise = self.ema_network.predict([samples0, tt], verbose=0, batch_size=1
614
+ )
615
+ samples = self.gdf_util.p_sample(
616
+ pred_noise, samples0, tt, clip_denoised=False
617
+ )
618
+ scaler = TSFeatureScaler()
619
+ scaled_samples = scaler.fit_transform(samples.numpy())
620
+ self._plot_step_grid(scaled_samples, i, time_steps, save_dir)
621
+
622
+ print(f"Saved {self.timesteps + 1} plots to {save_dir}/")
623
+
624
+ def _plot_step_grid(self, samples, step, time_steps, save_dir):
625
+
626
+ fig, axes = plt.subplots(4, 4, figsize=(20, 20))
627
+ fig.suptitle(f'Generated Samples at Step {step}', fontsize=16)
628
+
629
+ for idx in range(16):
630
+ row = idx // 4
631
+ col = idx % 4
632
+
633
+ for feature_idx in range(samples.shape[-1]):
634
+ axes[row, col].plot(
635
+ time_steps,
636
+ samples[idx, :, feature_idx],
637
+ label=f'Feature {feature_idx+1}',
638
+ alpha=0.8
639
+ )
640
+
641
+ axes[row, col].set_title(f'Sample {idx+1}')
642
+ axes[row, col].grid(True)
643
+ if idx % 4 == 0:
644
+ axes[row, col].set_ylabel('Value')
645
+ if idx >= 12:
646
+ axes[row, col].set_xlabel('Time Steps')
647
+ if idx == 15:
648
+ axes[row, col].legend()
649
+
650
+ plt.tight_layout()
651
+ plt.savefig(os.path.join(save_dir, f'step_{step:04d}.png'),
652
+ dpi=300, bbox_inches='tight')
653
+ plt.close()
654
+
655
+ def plot_denoise_detailed_process(self, save_dir='denoise_detailed_process'):
656
+
657
+ if os.path.exists(save_dir):
658
+ shutil.rmtree(save_dir)
659
+ os.makedirs(save_dir, exist_ok=True)
660
+
661
+ indices = tf.random.uniform(
662
+ shape=[8],
663
+ minval=0,
664
+ maxval=len(self.data),
665
+ dtype=tf.int32
666
+ )
667
+ x_0 = tf.cast(tf.gather(self.data, indices), tf.float32)
668
+ _, _, samples = self.gdf_util.q_sample(x_0, tf.fill([8], self.timesteps-1))
669
+ samples0 = samples
670
+
671
+ self._plot_step_comparison(
672
+ x_0.numpy(),
673
+ x_0.numpy(),
674
+ -1,
675
+ time_steps=np.arange(self.data.shape[1]),
676
+ save_dir=save_dir,
677
+ title="Original Data"
678
+ )
679
+
680
+ self._plot_step_comparison(
681
+ samples.numpy(),
682
+ samples.numpy(),
683
+ self.timesteps,
684
+ time_steps=np.arange(self.data.shape[1]),
685
+ save_dir=save_dir,
686
+ title="Initial Noisy Data"
687
+ )
688
+
689
+ for i in reversed(range(0, self.timesteps)):
690
+ print(f"Processing step {i}/{self.timesteps}")
691
+ tt = tf.fill([8], i)
692
+ pred_noise = self.ema_network.predict([samples0, tt], verbose=0, batch_size=8)
693
+ samples = self.gdf_util.p_sample(pred_noise, samples0, tt, clip_denoised=False)
694
+ scaler = TSFeatureScaler()
695
+
696
+ self._plot_step_comparison(
697
+ pred_noise,
698
+ scaler.fit_transform(samples.numpy()),
699
+ i,
700
+ time_steps=np.arange(self.data.shape[1]),
701
+ save_dir=save_dir,
702
+ title=f"Denoising Step {i}"
703
+ )
704
+
705
+ print(f"Visualization completed. Check {save_dir}/ for results.")
706
+
707
+ def _plot_step_comparison(self, noise_data, sample_data, step, time_steps, save_dir, title):
708
+
709
+ fig, axes = plt.subplots(2, 8, figsize=(24, 8))
710
+ fig.suptitle(f'{title}', fontsize=16)
711
+
712
+ for idx in range(8):
713
+ ax = axes[0, idx]
714
+ for feature_idx in range(noise_data.shape[-1]):
715
+ ax.plot(
716
+ time_steps,
717
+ noise_data[idx, :, feature_idx],
718
+ label=f'Feature {feature_idx+1}',
719
+ alpha=0.8
720
+ )
721
+ ax.set_title(f'Noise {idx+1}')
722
+ ax.grid(True)
723
+ if idx == 0:
724
+ ax.set_ylabel('Value')
725
+ if idx == 7:
726
+ ax.legend()
727
+
728
+ for idx in range(8):
729
+ ax = axes[1, idx]
730
+ for feature_idx in range(sample_data.shape[-1]):
731
+ ax.plot(
732
+ time_steps,
733
+ sample_data[idx, :, feature_idx],
734
+ label=f'Feature {feature_idx+1}',
735
+ alpha=0.8
736
+ )
737
+ ax.set_title(f'Sample {idx+1}')
738
+ ax.grid(True)
739
+ if idx == 0:
740
+ ax.set_ylabel('Value')
741
+ ax.set_xlabel('Time Steps')
742
+
743
+ plt.tight_layout()
744
+ plt.savefig(
745
+ os.path.join(save_dir, f'step_{step:04d}.png'),
746
+ dpi=300,
747
+ bbox_inches='tight'
748
+ )
749
+ plt.close()
750
+
751
+ def plot_noise_process(self, save_dir='noise_process'):
752
+
753
+ if os.path.exists(save_dir):
754
+ shutil.rmtree(save_dir)
755
+ os.makedirs(save_dir, exist_ok=True)
756
+
757
+ indices = tf.random.uniform(
758
+ shape=[16],
759
+ minval=0,
760
+ maxval=len(self.data),
761
+ dtype=tf.int32
762
+ )
763
+ x_start = tf.cast(tf.gather(self.data, indices), tf.float32)
764
+ print(f"Noising: Select 16 samples for visualization")
765
+
766
+ for t in range(self.timesteps):
767
+ print(f"Processing step {t}/{self.timesteps}")
768
+ _, _, x_noisy = self.gdf_util.q_sample(
769
+ x_start,
770
+ tf.fill([16], t))
771
+
772
+ fig, axes = plt.subplots(4, 4, figsize=(20, 20))
773
+ fig.suptitle(f'Noise Process at Step {t}', fontsize=16)
774
+
775
+ time_steps = np.arange(x_start.shape[1])
776
+
777
+ for idx in range(16):
778
+ row = idx // 4
779
+ col = idx % 4
780
+
781
+ for feature_idx in range(x_noisy.shape[-1]):
782
+ axes[row, col].plot(
783
+ time_steps,
784
+ x_noisy[idx, :, feature_idx],
785
+ label=f'Feature {feature_idx+1}',
786
+ alpha=0.8
787
+ )
788
+
789
+ axes[row, col].set_title(f'Sample {idx+1}')
790
+ axes[row, col].grid(True)
791
+ if idx % 4 == 0:
792
+ axes[row, col].set_ylabel('Value')
793
+ if idx >= 12:
794
+ axes[row, col].set_xlabel('Time Steps')
795
+ if idx == 15:
796
+ axes[row, col].legend()
797
+
798
+ plt.tight_layout()
799
+ plt.savefig(os.path.join(save_dir, f'step_{t:04d}.png'),
800
+ dpi=300, bbox_inches='tight')
801
+ plt.close()
802
+
803
+ print(f"Saved {self.timesteps} plots to {save_dir}/")
804
+
805
+ def plot_noise_process_app(self, num_samples=16):
806
+ """为应用程序创建加噪过程的动态可视化"""
807
+ # 验证num_samples是否为有效值
808
+ valid_sizes = [4, 9, 16, 25]
809
+ if num_samples not in valid_sizes:
810
+ raise ValueError(f"num_samples must be one of {valid_sizes}")
811
+
812
+ # 计算网格大小
813
+ grid_size = int(np.sqrt(num_samples))
814
+
815
+ # 随机选择样本
816
+ indices = tf.random.uniform(
817
+ shape=[num_samples],
818
+ minval=0,
819
+ maxval=len(self.data),
820
+ dtype=tf.int32
821
+ )
822
+ x_start = tf.cast(tf.gather(self.data, indices), tf.float32)
823
+
824
+ # 存储每一步的图像
825
+ frames = []
826
+ time_steps = np.arange(x_start.shape[1])
827
+
828
+ # 逐步添加噪声
829
+ for t in range(0, self.timesteps, max(1, self.timesteps // 10)):
830
+ # 添加噪声
831
+ _, _, x_noisy = self.gdf_util.q_sample(
832
+ x_start,
833
+ tf.fill([num_samples], t)
834
+ )
835
+
836
+ # 创建图形
837
+ fig, axes = plt.subplots(grid_size, grid_size, figsize=(5*grid_size, 5*grid_size))
838
+ fig.suptitle(f'Noise Process at Step {t}', fontsize=16)
839
+
840
+ colors = ['#0a9396', '#ee9b00', '#9b2226']
841
+
842
+ # 确保axes是二维数组
843
+ if grid_size == 2:
844
+ axes = axes.reshape(2, 2)
845
+
846
+ # 绘制每个样本
847
+ for idx in range(num_samples):
848
+ row = idx // grid_size
849
+ col = idx % grid_size
850
+
851
+ for feature_idx in range(x_noisy.shape[-1]):
852
+ axes[row, col].plot(
853
+ time_steps,
854
+ x_noisy[idx, :, feature_idx],
855
+ label=f'Feature {feature_idx+1}',
856
+ color=colors[feature_idx],
857
+ alpha=0.8
858
+ )
859
+
860
+ axes[row, col].set_title(f'Sample {idx+1}')
861
+ axes[row, col].grid(True)
862
+ if idx % grid_size == 0:
863
+ axes[row, col].set_ylabel('Value')
864
+ if idx >= num_samples - grid_size:
865
+ axes[row, col].set_xlabel('Time Steps')
866
+ if idx == num_samples - 1:
867
+ axes[row, col].legend()
868
+
869
+ plt.tight_layout()
870
+
871
+ # 将图形转换为图像
872
+ fig.canvas.draw()
873
+ image = np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8)
874
+ image = image.reshape(fig.canvas.get_width_height()[::-1] + (3,))
875
+ frames.append(image)
876
+
877
+ plt.close()
878
+
879
+ return frames
880
+
881
+ def plot_denoise_process_app(self, num_samples=16):
882
+ """为应用程序创建去噪过程的动态可视化"""
883
+ # 验证num_samples是否为有效值
884
+ valid_sizes = [4, 9, 16, 25]
885
+ if num_samples not in valid_sizes:
886
+ raise ValueError(f"num_samples must be one of {valid_sizes}")
887
+
888
+ # 计算网格大小
889
+ grid_size = int(np.sqrt(num_samples))
890
+
891
+ # 随机选择样本
892
+ indices = tf.random.uniform(
893
+ shape=[num_samples],
894
+ minval=0,
895
+ maxval=len(self.data),
896
+ dtype=tf.int32
897
+ )
898
+ x_0 = tf.cast(tf.gather(self.data, indices), tf.float32)
899
+ _, _, samples = self.gdf_util.q_sample(x_0, tf.fill([num_samples], self.timesteps-1))
900
+ samples0 = samples
901
+
902
+ # 存储每一步的图像
903
+ frames = []
904
+ time_steps = np.arange(self.data.shape[1])
905
+
906
+ # 添加原始数据的图像
907
+ frames.append(self._plot_step_grid_app(x_0.numpy(), self.timesteps, time_steps, grid_size))
908
+
909
+ # 逐步去噪
910
+ for i in reversed(range(0, self.timesteps)):
911
+ if i % max(1, self.timesteps // 10) == 0:
912
+ tt = tf.fill([num_samples], i)
913
+ pred_noise = self.ema_network.predict([samples0, tt], verbose=0, batch_size=num_samples)
914
+ samples = self.gdf_util.p_sample(pred_noise, samples0, tt, clip_denoised=False)
915
+ scaler = TSFeatureScaler()
916
+ scaled_samples = scaler.fit_transform(samples.numpy())
917
+ frames.append(self._plot_step_grid_app(scaled_samples, i, time_steps, grid_size))
918
+
919
+ return frames
920
+
921
+ def _plot_step_grid_app(self, samples, step, time_steps, grid_size):
922
+ """辅助函数:为应用程序创建单个时间步的网格图"""
923
+ fig, axes = plt.subplots(grid_size, grid_size, figsize=(5*grid_size, 5*grid_size))
924
+ fig.suptitle(f'Generated Samples at Step {step}', fontsize=16)
925
+
926
+ colors = ['#0a9396', '#ee9b00', '#9b2226']
927
+
928
+ # 确保axes是二维数组
929
+ if grid_size == 2:
930
+ axes = axes.reshape(2, 2)
931
+
932
+ # 绘制每个样本
933
+ num_samples = grid_size * grid_size
934
+ for idx in range(num_samples):
935
+ row = idx // grid_size
936
+ col = idx % grid_size
937
+
938
+ for feature_idx in range(samples.shape[-1]):
939
+ axes[row, col].plot(
940
+ time_steps,
941
+ samples[idx, :, feature_idx],
942
+ label=f'Feature {feature_idx+1}',
943
+ color=colors[feature_idx],
944
+ alpha=0.8
945
+ )
946
+
947
+ axes[row, col].set_title(f'Sample {idx+1}')
948
+ axes[row, col].grid(True)
949
+ if idx % grid_size == 0:
950
+ axes[row, col].set_ylabel('Value')
951
+ if idx >= num_samples - grid_size:
952
+ axes[row, col].set_xlabel('Time Steps')
953
+ if idx == num_samples - 1:
954
+ axes[row, col].legend()
955
+
956
+ plt.tight_layout()
957
+
958
+ # 将图形转换为图像
959
+ fig.canvas.draw()
960
+ image = np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8)
961
+ image = image.reshape(fig.canvas.get_width_height()[::-1] + (3,))
962
+
963
+ plt.close()
964
+ return image
965
+
GAN/timegan.py ADDED
@@ -0,0 +1,601 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import tensorflow as tf
2
+ from tensorflow import keras
3
+ from tensorflow.python.types.core import TensorLike
4
+ import numpy as np
5
+ import numpy.typing as npt
6
+ from tqdm import tqdm, trange
7
+ from collections import OrderedDict
8
+ import typing as T
9
+
10
+ import logging
11
+
12
+ from GAN.zoo import BasicRecurrentArchitecture
13
+
14
+ logger = logging.getLogger("models")
15
+ logger.setLevel(logging.DEBUG)
16
+
17
+
18
+ class LossTracker(OrderedDict):
19
+ """
20
+ Dictionary of lists, extends python OrderedDict.
21
+ Example: Given {'loss_a': [1], 'loss_b': [2]}, adding key='loss_a' with value=0.7
22
+ gives {'loss_a': [1, 0.7], 'loss_b': [2]}, and adding key='loss_c' with value=1.2
23
+ gives {'loss_a': [1, 0.7], 'loss_b': [2], 'loss_c': [1.2]}
24
+ """
25
+
26
+ def __setitem__(self, key: T.Any, value: T.Any) -> None:
27
+ try:
28
+ # Assumes the key already exists
29
+ # and the value is a list [oldest_value, another_old, ...]
30
+ # key -> [oldest_value, another_old, ..., new_value]
31
+ self[key].append(value)
32
+ # If there is no key, add key -> [new_value]
33
+ except KeyError:
34
+ # key -> [new_value]
35
+ super(LossTracker, self).__setitem__(key, [value])
36
+
37
+ def to_numpy(self) -> npt.NDArray:
38
+ """
39
+ :return 2d vector of losses
40
+ """
41
+ _losses = np.array([np.array(v) for v in self.values() if isinstance(v, list)])
42
+ return _losses
43
+
44
+ def labels(self) -> T.List:
45
+ """
46
+ :return list of keys
47
+ """
48
+ return list(self.keys())
49
+
50
+
51
+
52
+ class TimeGAN(keras.Model):
53
+ """
54
+ Time-series Generative Adversarial Networks (TimeGAN)
55
+
56
+ Reference: Jinsung Yoon, Daniel Jarrett, Mihaela van der Schaar,
57
+ "Time-series Generative Adversarial Networks,"
58
+ Neural Information Processing Systems (NeurIPS), 2019.
59
+
60
+ Paper link: https://papers.nips.cc/paper/8789-time-series-generative-adversarial-networks
61
+ """
62
+
63
+ def __init__(
64
+ self,
65
+ seq_len: int = 24,
66
+ module: str = "gru",
67
+ hidden_dim: int = 24,
68
+ n_features: int = 6,
69
+ n_layers: int = 3,
70
+ batch_size: int = 256,
71
+ gamma: float = 1.0,
72
+ ) -> None:
73
+ super().__init__()
74
+ self.seq_len = seq_len
75
+ self.hidden_dim = hidden_dim
76
+ self.dim = n_features
77
+
78
+ assert module in ["gru", "lstm", "lstmLN"]
79
+ self.module = module
80
+
81
+ self.n_layers = n_layers
82
+
83
+ self.batch_size = batch_size
84
+
85
+ self.gamma = gamma
86
+
87
+ # ----------------------------
88
+ # Basic Architectures
89
+ # ----------------------------
90
+ self.embedder = BasicRecurrentArchitecture(
91
+ hidden_dim=self.hidden_dim,
92
+ output_dim=self.hidden_dim,
93
+ n_layers=self.n_layers,
94
+ network_type=self.module,
95
+ name="Embedder",
96
+ ).build()
97
+
98
+ self.recovery = BasicRecurrentArchitecture(
99
+ hidden_dim=self.hidden_dim,
100
+ output_dim=self.dim,
101
+ n_layers=self.n_layers,
102
+ network_type=self.module,
103
+ name="Recovery",
104
+ ).build()
105
+
106
+ self.supervisor = BasicRecurrentArchitecture(
107
+ hidden_dim=self.hidden_dim,
108
+ output_dim=self.hidden_dim,
109
+ n_layers=self.n_layers,
110
+ network_type=self.module,
111
+ name="Supervisor",
112
+ ).build()
113
+
114
+ self.discriminator = BasicRecurrentArchitecture(
115
+ hidden_dim=self.hidden_dim,
116
+ output_dim=1,
117
+ n_layers=self.n_layers,
118
+ network_type=self.module,
119
+ name="Discriminator",
120
+ ).build()
121
+
122
+ self.generator_aux = BasicRecurrentArchitecture(
123
+ hidden_dim=self.hidden_dim,
124
+ output_dim=self.hidden_dim,
125
+ n_layers=self.n_layers,
126
+ network_type=self.module,
127
+ name="Generator",
128
+ ).build()
129
+
130
+ # ----------------------------
131
+ # Optimizers: call .compile() to set them
132
+ # ----------------------------
133
+ self.autoencoder_opt = keras.optimizers.Adam()
134
+ self.adversarialsup_opt = keras.optimizers.Adam()
135
+ self.generator_opt = keras.optimizers.Adam()
136
+ self.embedder_opt = keras.optimizers.Adam()
137
+ self.discriminator_opt = keras.optimizers.Adam()
138
+ # ----------------------------
139
+ # Loss functions: call .compile() to set them
140
+ # ----------------------------
141
+ self._mse = keras.losses.MeanSquaredError()
142
+ self._bce = keras.losses.BinaryCrossentropy()
143
+
144
+ # --------------------------
145
+ # All losses: will be populated in .fit()
146
+ # --------------------------
147
+ self.training_losses_history = LossTracker()
148
+
149
+ # --------------------------
150
+ # Synthetic data generation during training: will be populated in .fit()
151
+ # --------------------------
152
+ self.synthetic_data_generated_in_training = dict()
153
+
154
+ def compile(
155
+ self,
156
+ d_optimizer: keras.optimizers.Optimizer = keras.optimizers.Adam(), # keras.optimizers.legacy.Adam()
157
+ g_optimizer: keras.optimizers.Optimizer = keras.optimizers.Adam(),
158
+ emb_optimizer: keras.optimizers.Optimizer = keras.optimizers.Adam(),
159
+ supgan_optimizer: keras.optimizers.Optimizer = keras.optimizers.Adam(),
160
+ ae_optimizer: keras.optimizers.Optimizer = keras.optimizers.Adam(),
161
+ emb_loss: keras.losses.Loss = keras.losses.MeanSquaredError(),
162
+ clf_loss: keras.losses.Loss = keras.losses.BinaryCrossentropy(),
163
+ ) -> None:
164
+ """
165
+ Assign optimizers and loss functions.
166
+
167
+ :param d_optimizer: An optimizer for the GAN's discriminator
168
+ :param g_optimizer: An optimizer for the GAN's generator
169
+ :param emb_optimizer: An optimizer for the GAN's embedder
170
+ :param supgan_optimizer: An optimizer for the adversarial supervised network
171
+ :param ae_optimizer: An optimizer for the autoencoder network
172
+ :param emb_loss: A loss function for the embedding recovery
173
+ :param clf_loss: A loss function for the discriminator task
174
+ :return: None
175
+ """
176
+ # ----------------------------
177
+ # Optimizers
178
+ # ----------------------------
179
+ self.autoencoder_opt = ae_optimizer
180
+ self.adversarialsup_opt = supgan_optimizer
181
+ self.generator_opt = g_optimizer
182
+ self.embedder_opt = emb_optimizer
183
+ self.discriminator_opt = d_optimizer
184
+ # ----------------------------
185
+ # Loss functions
186
+ # ----------------------------
187
+ self._mse = emb_loss
188
+ self._bce = clf_loss
189
+
190
+ def _define_timegan(self) -> None:
191
+ # --------------------------------
192
+ # Data and Noise Inputs
193
+ # --------------------------------
194
+ X = keras.layers.Input(
195
+ shape=[self.seq_len, self.dim], batch_size=self.batch_size, name="RealData"
196
+ )
197
+
198
+ Z = keras.layers.Input(
199
+ shape=[self.seq_len, self.dim],
200
+ batch_size=self.batch_size,
201
+ name="RandomNoise",
202
+ )
203
+
204
+ # --------------------------------
205
+ # Autoencoder: Embedder + Recovery
206
+ # --------------------------------
207
+ H = self.embedder(X)
208
+ X_tilde = self.recovery(H)
209
+
210
+ self.autoencoder = keras.models.Model(
211
+ inputs=X, outputs=X_tilde, name="Autoencoder"
212
+ )
213
+ self.autoencoder.summary()
214
+
215
+ # ---------------------------------
216
+ # Adversarial Supervised
217
+ # ---------------------------------
218
+ E_Hat = self.generator_aux(Z)
219
+ H_hat = self.supervisor(E_Hat)
220
+ Y_fake = self.discriminator(H_hat)
221
+
222
+ self.adversarial_supervised = keras.models.Model(
223
+ inputs=Z, outputs=Y_fake, name="AdversarialSupervised"
224
+ )
225
+ self.adversarial_supervised.summary()
226
+
227
+ # ---------------------------------
228
+ # Adversarial embedded in latent space
229
+ # ---------------------------------
230
+ Y_fake_e = self.discriminator(E_Hat)
231
+
232
+ self.adversarial_embedded = keras.models.Model(
233
+ inputs=Z, outputs=Y_fake_e, name="AdversarialEmbedded"
234
+ )
235
+ self.adversarial_embedded.summary()
236
+
237
+ # ---------------------------------
238
+ # Synthetic data generator
239
+ # ---------------------------------
240
+ X_hat = self.recovery(H_hat)
241
+ self.generator = keras.models.Model(
242
+ inputs=Z, outputs=X_hat, name="FinalGenerator"
243
+ )
244
+ self.generator.summary()
245
+
246
+ # --------------------------------
247
+ # Discriminator
248
+ # --------------------------------
249
+ Y_real = self.discriminator(H)
250
+ self.discriminator_model = keras.models.Model(
251
+ inputs=X, outputs=Y_real, name="FinalDiscriminator"
252
+ )
253
+ self.discriminator_model.summary()
254
+
255
+ @tf.function
256
+ def _train_autoencoder(
257
+ self, X: TensorLike, optimizer: keras.optimizers.Optimizer
258
+ ) -> float:
259
+ """
260
+ 1. Embedding network training: minimize E_loss0
261
+ """
262
+ with tf.GradientTape() as tape:
263
+ X_tilde = self.autoencoder(X)
264
+ E_loss_T0 = self._mse(X, X_tilde)
265
+ E_loss0 = 10.0 * tf.sqrt(E_loss_T0)
266
+
267
+ e_vars = self.embedder.trainable_variables
268
+ r_vars = self.recovery.trainable_variables
269
+ all_trainable = e_vars + r_vars
270
+
271
+ gradients = tape.gradient(E_loss0, all_trainable)
272
+ optimizer.apply_gradients(zip(gradients, all_trainable))
273
+ return E_loss0
274
+
275
+ @tf.function
276
+ def _train_supervisor(
277
+ self, X: TensorLike, optimizer: keras.optimizers.Optimizer
278
+ ) -> float:
279
+ """
280
+ 2. Training with supervised loss only: minimize G_loss_S
281
+ """
282
+ with tf.GradientTape() as tape:
283
+ H = self.embedder(X)
284
+ H_hat_supervised = self.supervisor(H)
285
+ G_loss_S = self._mse(H[:, 1:, :], H_hat_supervised[:, :-1, :])
286
+
287
+ g_vars = self.generator.trainable_variables
288
+ s_vars = self.supervisor.trainable_variables
289
+ all_trainable = g_vars + s_vars
290
+ gradients = tape.gradient(G_loss_S, all_trainable)
291
+ apply_grads = [
292
+ (grad, var)
293
+ for (grad, var) in zip(gradients, all_trainable)
294
+ if grad is not None
295
+ ]
296
+ optimizer.apply_gradients(apply_grads)
297
+ return G_loss_S
298
+
299
+ @tf.function
300
+ def _train_generator(
301
+ self, X: TensorLike, Z: TensorLike, optimizer: keras.optimizers.Optimizer
302
+ ) -> T.Tuple[float, float, float, float, float]:
303
+ """
304
+ 3. Joint training (Generator training twice more than discriminator training): minimize G_loss
305
+ """
306
+ with tf.GradientTape() as tape:
307
+ # 1. Adversarial loss
308
+ Y_fake = self.adversarial_supervised(Z)
309
+ G_loss_U = self._bce(y_true=tf.ones_like(Y_fake), y_pred=Y_fake)
310
+
311
+ Y_fake_e = self.adversarial_embedded(Z)
312
+ G_loss_U_e = self._bce(y_true=tf.ones_like(Y_fake_e), y_pred=Y_fake_e)
313
+ # 2. Supervised loss
314
+ H = self.embedder(X)
315
+ H_hat_supervised = self.supervisor(H)
316
+ G_loss_S = self._mse(H[:, 1:, :], H_hat_supervised[:, :-1, :])
317
+
318
+ # 3. Two Moments
319
+ X_hat = self.generator(Z)
320
+ G_loss_V = self._compute_generator_moments_loss(X, X_hat)
321
+
322
+ # 4. Summation
323
+ G_loss = (
324
+ G_loss_U
325
+ + self.gamma * G_loss_U_e
326
+ + 100 * tf.sqrt(G_loss_S)
327
+ + 100 * G_loss_V
328
+ )
329
+
330
+ g_vars = self.generator_aux.trainable_variables
331
+ s_vars = self.supervisor.trainable_variables
332
+ all_trainable = g_vars + s_vars
333
+ gradients = tape.gradient(G_loss, all_trainable)
334
+ apply_grads = [
335
+ (grad, var)
336
+ for (grad, var) in zip(gradients, all_trainable)
337
+ if grad is not None
338
+ ]
339
+ optimizer.apply_gradients(apply_grads)
340
+ return G_loss_U, G_loss_U_e, G_loss_S, G_loss_V, G_loss
341
+
342
+ @tf.function
343
+ def _train_embedder(
344
+ self, X: TensorLike, optimizer: keras.optimizers.Optimizer
345
+ ) -> T.Tuple[float, float]:
346
+ """
347
+ Train embedder during joint training: minimize E_loss
348
+ """
349
+ with tf.GradientTape() as tape:
350
+ # Supervised Loss
351
+ H = self.embedder(X)
352
+ H_hat_supervised = self.supervisor(H)
353
+ G_loss_S = self._mse(H[:, 1:, :], H_hat_supervised[:, :-1, :])
354
+
355
+ # Reconstruction Loss
356
+ X_tilde = self.autoencoder(X)
357
+ E_loss_T0 = self._mse(X, X_tilde)
358
+ E_loss0 = 10 * tf.sqrt(E_loss_T0)
359
+
360
+ E_loss = E_loss0 + 0.1 * G_loss_S
361
+
362
+ e_vars = self.embedder.trainable_variables
363
+ r_vars = self.recovery.trainable_variables
364
+ all_trainable = e_vars + r_vars
365
+ gradients = tape.gradient(E_loss, all_trainable)
366
+ optimizer.apply_gradients(zip(gradients, all_trainable))
367
+ return E_loss, E_loss_T0
368
+
369
+ @tf.function
370
+ def _train_discriminator(
371
+ self, X: TensorLike, Z: TensorLike, optimizer: keras.optimizers.Optimizer
372
+ ) -> float:
373
+ """
374
+ minimize D_loss
375
+ """
376
+ with tf.GradientTape() as tape:
377
+ D_loss = self._check_discriminator_loss(X, Z)
378
+
379
+ d_vars = self.discriminator.trainable_variables
380
+ gradients = tape.gradient(D_loss, d_vars)
381
+ optimizer.apply_gradients(zip(gradients, d_vars))
382
+ return D_loss
383
+
384
+ @staticmethod
385
+ def _compute_generator_moments_loss(
386
+ y_true: TensorLike, y_pred: TensorLike
387
+ ) -> float:
388
+ """
389
+ :param y_true: TensorLike
390
+ :param y_pred: TensorLike
391
+ :return G_loss_V: float
392
+ """
393
+ _eps = 1e-6
394
+ y_true_mean, y_true_var = tf.nn.moments(x=y_true, axes=[0])
395
+ y_pred_mean, y_pred_var = tf.nn.moments(x=y_pred, axes=[0])
396
+ # G_loss_V2
397
+ g_loss_mean = tf.reduce_mean(abs(y_true_mean - y_pred_mean))
398
+ # G_loss_V1
399
+ g_loss_var = tf.reduce_mean(
400
+ abs(tf.sqrt(y_true_var + _eps) - tf.sqrt(y_pred_var + _eps))
401
+ )
402
+ # G_loss_V = G_loss_V1 + G_loss_V2
403
+ return g_loss_mean + g_loss_var
404
+
405
+ def _check_discriminator_loss(self, X: TensorLike, Z: TensorLike) -> float:
406
+ """
407
+ :param X: TensorLike
408
+ :param Z: TensorLike
409
+ :return D_loss: float
410
+ """
411
+ # Loss on false negatives
412
+ Y_real = self.discriminator_model(X)
413
+ D_loss_real = self._bce(y_true=tf.ones_like(Y_real), y_pred=Y_real)
414
+
415
+ # Loss on false positives
416
+ Y_fake = self.adversarial_supervised(Z)
417
+ D_loss_fake = self._bce(y_true=tf.zeros_like(Y_fake), y_pred=Y_fake)
418
+
419
+ Y_fake_e = self.adversarial_embedded(Z)
420
+ D_loss_fake_e = self._bce(y_true=tf.zeros_like(Y_fake_e), y_pred=Y_fake_e)
421
+
422
+ D_loss = D_loss_real + D_loss_fake + self.gamma * D_loss_fake_e
423
+ return D_loss
424
+
425
+ def _generate_noise(self) -> TensorLike:
426
+ """
427
+ Random vector generation
428
+ :return Z, generated random vector
429
+ """
430
+ while True:
431
+ yield np.random.uniform(low=0, high=1, size=(self.seq_len, self.dim))
432
+
433
+ def get_noise_batch(self) -> T.Iterator:
434
+ """
435
+ Return an iterator of random noise vectors
436
+ """
437
+ return iter(
438
+ tf.data.Dataset.from_generator(
439
+ self._generate_noise, output_types=tf.float32
440
+ )
441
+ .batch(self.batch_size)
442
+ .repeat()
443
+ )
444
+
445
+ def _get_data_batch(self, data: TensorLike, n_windows: int) -> T.Iterator:
446
+ """
447
+ Return an iterator of shuffled input data
448
+ """
449
+ data = tf.convert_to_tensor(data, dtype=tf.float32)
450
+ return iter(
451
+ tf.data.Dataset.from_tensor_slices(data)
452
+ .shuffle(buffer_size=n_windows)
453
+ .batch(self.batch_size)
454
+ .repeat()
455
+ )
456
+
457
+ def fit(
458
+ self,
459
+ data: T.Union[TensorLike, tf.data.Dataset],
460
+ epochs: int,
461
+ checkpoints_interval: T.Optional[int] = None,
462
+ generate_synthetic: T.Tuple = (),
463
+ *args,
464
+ **kwargs,
465
+ ):
466
+ """
467
+ :param data: TensorLike, the training data
468
+ :param epochs: int, the number of epochs for the training loops
469
+ :param checkpoints_interval: int, the interval for printing out loss values
470
+ (loss values will be print out every 'checkpoints_interval' epochs)
471
+ Default: None (no print out)
472
+ :param generate_synthetic: list of int, a list of epoch numbers when synthetic data samples are generated
473
+ Default: [] (no generation)
474
+ :return None
475
+ """
476
+ assert not (
477
+ self.autoencoder_opt is None
478
+ or self.adversarialsup_opt is None
479
+ or self.generator_opt is None
480
+ or self.embedder_opt is None
481
+ or self.discriminator_opt is None
482
+ ), "One of the optimizers is not defined. Please call .compile() to set them"
483
+ assert not (
484
+ self._mse is None or self._bce is None
485
+ ), "One of the loss functions is not defined. Please call .compile() to set them"
486
+
487
+ # take tf.data.Dataset | TensorLike
488
+ if isinstance(data, tf.data.Dataset):
489
+ batches = iter(data.repeat())
490
+ else:
491
+ batches = self._get_data_batch(data, n_windows=len(data))
492
+
493
+ # Define the model
494
+ self._define_timegan()
495
+
496
+ # 1. Embedding network training
497
+ logger.info("Start Embedding Network Training")
498
+
499
+ for epoch in tqdm(range(epochs), desc="Autoencoder - training"):
500
+ X_ = next(batches)
501
+ step_e_loss_0 = self._train_autoencoder(X_, self.autoencoder_opt)
502
+
503
+ # Checkpoint
504
+ if checkpoints_interval is not None and epoch % checkpoints_interval == 0:
505
+ logger.info(f"step: {epoch}/{epochs}, e_loss: {step_e_loss_0}")
506
+ self.training_losses_history["autoencoder"] = float(step_e_loss_0)
507
+
508
+ logger.info("Finished Embedding Network Training")
509
+
510
+ # 2. Training only with supervised loss
511
+ logger.info("Start Training with Supervised Loss Only")
512
+
513
+ # Adversarial Supervised network training
514
+ for epoch in tqdm(range(epochs), desc="Adversarial Supervised - training"):
515
+ X_ = next(batches)
516
+ step_g_loss_s = self._train_supervisor(X_, self.adversarialsup_opt)
517
+
518
+ # Checkpoint
519
+ if checkpoints_interval is not None and epoch % checkpoints_interval == 0:
520
+ logger.info(
521
+ f"step: {epoch}/{epochs}, s_loss: {np.round(np.sqrt(step_g_loss_s), 4)}"
522
+ )
523
+ self.training_losses_history["adversarial_supervised"] = float(
524
+ np.sqrt(step_g_loss_s)
525
+ )
526
+
527
+ logger.info("Finished Training with Supervised Loss Only")
528
+
529
+ # 3. Joint Training
530
+ logger.info("Start Joint Training")
531
+
532
+ # GAN with embedding network training
533
+ for epoch in tqdm(range(epochs), desc="GAN with embedding - training"):
534
+ # Generator training (twice more than discriminator training)
535
+ for kk in range(2):
536
+ X_ = next(batches)
537
+ Z_ = next(self.get_noise_batch())
538
+ # --------------------------
539
+ # Train the generator
540
+ # --------------------------
541
+ (
542
+ step_g_loss_u,
543
+ step_g_loss_u_e,
544
+ step_g_loss_s,
545
+ step_g_loss_v,
546
+ step_g_loss,
547
+ ) = self._train_generator(X_, Z_, self.generator_opt)
548
+
549
+ # --------------------------
550
+ # Train the embedder
551
+ # --------------------------
552
+ _, step_e_loss_t0 = self._train_embedder(X_, self.embedder_opt)
553
+
554
+ X_ = next(batches)
555
+ Z_ = next(self.get_noise_batch())
556
+ step_d_loss = self._check_discriminator_loss(X_, Z_)
557
+ if step_d_loss > 0.15:
558
+ logger.info(
559
+ "Train Discriminator (discriminator does not work well yet)"
560
+ )
561
+ step_d_loss = self._train_discriminator(X_, Z_, self.discriminator_opt)
562
+
563
+ # Print multiple checkpoints
564
+ if checkpoints_interval is not None and epoch % checkpoints_interval == 0:
565
+ logger.info(
566
+ f"""step: {epoch}/{epochs},
567
+ d_loss: {np.round(step_d_loss, 4)},
568
+ g_loss_u: {np.round(step_g_loss_u, 4)},
569
+ g_loss_u_e: {np.round(step_g_loss_u_e, 4)},
570
+ g_loss_s: {np.round(np.sqrt(step_g_loss_s), 4)},
571
+ g_loss_v: {np.round(step_g_loss_v, 4)},
572
+ g_loss_v: {np.round(step_g_loss, 4)},
573
+ e_loss_t0: {np.round(np.sqrt(step_e_loss_t0), 4)}"""
574
+ )
575
+ self.training_losses_history["discriminator"] = float(step_d_loss)
576
+ self.training_losses_history["generator_u"] = float(step_g_loss_u)
577
+ self.training_losses_history["generator_u_e"] = float(step_g_loss_u_e)
578
+ self.training_losses_history["generator_v"] = float(step_g_loss_v)
579
+ self.training_losses_history["generator_s"] = float(np.sqrt(step_g_loss_s))
580
+ self.training_losses_history["generator"] = float(step_g_loss)
581
+ self.training_losses_history["embedder"] = float(np.sqrt(step_e_loss_t0))
582
+
583
+ # Synthetic data generation
584
+ if epoch in generate_synthetic:
585
+ _sample = self.generate(n_samples=len(data))
586
+ self.synthetic_data_generated_in_training[epoch] = _sample
587
+
588
+ logger.info("Finished Joint Training")
589
+ return
590
+
591
+ def generate(self, n_samples: int) -> TensorLike:
592
+ """
593
+ Generate synthetic time series
594
+ """
595
+ steps = n_samples // self.batch_size + 1
596
+ data = []
597
+ for _ in trange(steps, desc="Synthetic data generation"):
598
+ Z_ = next(self.get_noise_batch())
599
+ records = self.generator(Z_)
600
+ data.append(records)
601
+ return np.array(np.vstack(data))[:n_samples]
GAN/timevae.py ADDED
@@ -0,0 +1,430 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, warnings
2
+ warnings.filterwarnings('ignore')
3
+
4
+ from abc import ABC, abstractmethod
5
+ import numpy as np
6
+ import joblib
7
+ import tensorflow as tf
8
+ from tensorflow.keras import backend as K
9
+ from tensorflow.keras.layers import Conv1D, Flatten, Dense, Conv1DTranspose, Reshape, Input, Layer
10
+ from tensorflow.keras.models import Model
11
+ from tensorflow.keras.optimizers import Adam
12
+ from tensorflow.keras.metrics import Mean
13
+ from tensorflow.keras.backend import random_normal
14
+
15
+ class Sampling(Layer):
16
+ """Uses (z_mean, z_log_var) to sample z, the vector encoding a digit."""
17
+
18
+ def call(self, inputs):
19
+ z_mean, z_log_var = inputs
20
+ batch = tf.shape(z_mean)[0]
21
+ dim = tf.shape(z_mean)[1]
22
+ epsilon = random_normal(shape=(batch, dim))
23
+ return z_mean + tf.exp(0.5 * z_log_var) * epsilon
24
+
25
+ class BaseVariationalAutoencoder(Model, ABC):
26
+ def __init__(self,
27
+ seq_len,
28
+ feat_dim,
29
+ latent_dim,
30
+ reconstruction_wt=3.0,
31
+ **kwargs):
32
+ super(BaseVariationalAutoencoder, self).__init__(**kwargs)
33
+ self.seq_len = seq_len
34
+ self.feat_dim = feat_dim
35
+ self.latent_dim = latent_dim
36
+ self.reconstruction_wt = reconstruction_wt
37
+ self.total_loss_tracker = Mean(name="total_loss")
38
+ self.reconstruction_loss_tracker = Mean(name="reconstruction_loss")
39
+ self.kl_loss_tracker = Mean(name="kl_loss")
40
+
41
+ self.encoder = None
42
+ self.decoder = None
43
+
44
+ def call(self, X):
45
+ z_mean, _, _ = self.encoder(X)
46
+ x_decoded = self.decoder(z_mean)
47
+ if len(x_decoded.shape) == 1: x_decoded = x_decoded.reshape((1, -1))
48
+ return x_decoded
49
+
50
+ def get_num_trainable_variables(self):
51
+ trainableParams = int(np.sum([np.prod(v.get_shape()) for v in self.trainable_weights]))
52
+ nonTrainableParams = int(np.sum([np.prod(v.get_shape()) for v in self.non_trainable_weights]))
53
+ totalParams = trainableParams + nonTrainableParams
54
+ return trainableParams, nonTrainableParams, totalParams
55
+
56
+ def get_prior_samples(self, num_samples):
57
+ Z = np.random.randn(num_samples, self.latent_dim)
58
+ samples = self.decoder.predict(Z)
59
+ return samples
60
+
61
+ def get_prior_samples_given_Z(self, Z):
62
+ samples = self.decoder.predict(Z)
63
+ return samples
64
+
65
+ @abstractmethod
66
+ def _get_encoder(self, **kwargs):
67
+ raise NotImplementedError
68
+
69
+ @abstractmethod
70
+ def _get_decoder(self, **kwargs):
71
+ raise NotImplementedError
72
+
73
+ def summary(self):
74
+ self.encoder.summary()
75
+ self.decoder.summary()
76
+
77
+ def _get_reconstruction_loss(self, X, X_recons):
78
+ def get_reconst_loss_by_axis(X, X_c, axis):
79
+ x_r = tf.reduce_mean(X, axis=axis)
80
+ x_c_r = tf.reduce_mean(X_recons, axis=axis)
81
+ err = tf.math.squared_difference(x_r, x_c_r)
82
+ loss = tf.reduce_sum(err)
83
+ return loss
84
+
85
+ # overall
86
+ err = tf.math.squared_difference(X, X_recons)
87
+ reconst_loss = tf.reduce_sum(err)
88
+
89
+ reconst_loss += get_reconst_loss_by_axis(X, X_recons, axis=[2]) # by time axis
90
+ # reconst_loss += get_reconst_loss_by_axis(X, X_recons, axis=[1]) # by feature axis
91
+ return reconst_loss
92
+
93
+ def train_step(self, X):
94
+ with tf.GradientTape() as tape:
95
+ z_mean, z_log_var, z = self.encoder(X)
96
+
97
+ reconstruction = self.decoder(z)
98
+
99
+ reconstruction_loss = self._get_reconstruction_loss(X, reconstruction)
100
+
101
+ kl_loss = -0.5 * (1 + z_log_var - tf.square(z_mean) - tf.exp(z_log_var))
102
+ kl_loss = tf.reduce_sum(tf.reduce_sum(kl_loss, axis=1))
103
+ # kl_loss = kl_loss / self.latent_dim
104
+
105
+ total_loss = self.reconstruction_wt * reconstruction_loss + kl_loss
106
+
107
+ grads = tape.gradient(total_loss, self.trainable_weights)
108
+
109
+ self.optimizer.apply_gradients(zip(grads, self.trainable_weights))
110
+
111
+ self.total_loss_tracker.update_state(total_loss)
112
+ self.reconstruction_loss_tracker.update_state(reconstruction_loss)
113
+ self.kl_loss_tracker.update_state(kl_loss)
114
+
115
+ return {
116
+ "loss": self.total_loss_tracker.result(),
117
+ "reconstruction_loss": self.reconstruction_loss_tracker.result(),
118
+ "kl_loss": self.kl_loss_tracker.result(),
119
+ }
120
+
121
+ def test_step(self, X):
122
+ z_mean, z_log_var, z = self.encoder(X)
123
+ reconstruction = self.decoder(z)
124
+ reconstruction_loss = self._get_reconstruction_loss(X, reconstruction)
125
+
126
+ kl_loss = -0.5 * (1 + z_log_var - tf.square(z_mean) - tf.exp(z_log_var))
127
+ kl_loss = tf.reduce_sum(tf.reduce_sum(kl_loss, axis=1))
128
+ # kl_loss = kl_loss / self.latent_dim
129
+
130
+ total_loss = self.reconstruction_wt * reconstruction_loss + kl_loss
131
+
132
+ self.total_loss_tracker.update_state(total_loss)
133
+ self.reconstruction_loss_tracker.update_state(reconstruction_loss)
134
+ self.kl_loss_tracker.update_state(kl_loss)
135
+
136
+ return {
137
+ "loss": self.total_loss_tracker.result(),
138
+ "reconstruction_loss": self.reconstruction_loss_tracker.result(),
139
+ "kl_loss": self.kl_loss_tracker.result(),
140
+ }
141
+
142
+ def save_weights(self, model_dir, file_pref):
143
+ encoder_wts = self.encoder.get_weights()
144
+ decoder_wts = self.decoder.get_weights()
145
+ joblib.dump(encoder_wts, os.path.join(model_dir, f'{file_pref}encoder_wts.h5'))
146
+ joblib.dump(decoder_wts, os.path.join(model_dir, f'{file_pref}decoder_wts.h5'))
147
+
148
+ def load_weights(self, model_dir, file_pref):
149
+ encoder_wts = joblib.load(os.path.join(model_dir, f'{file_pref}encoder_wts.h5'))
150
+ decoder_wts = joblib.load(os.path.join(model_dir, f'{file_pref}decoder_wts.h5'))
151
+
152
+ self.encoder.set_weights(encoder_wts)
153
+ self.decoder.set_weights(decoder_wts)
154
+
155
+ def save(self, model_dir, file_pref):
156
+ self.save_weights(model_dir, file_pref)
157
+ dict_params = {
158
+
159
+ 'seq_len': self.seq_len,
160
+ 'feat_dim': self.feat_dim,
161
+ 'latent_dim': self.latent_dim,
162
+ 'reconstruction_wt': self.reconstruction_wt,
163
+ 'hidden_layer_sizes': self.hidden_layer_sizes,
164
+ }
165
+ params_file = os.path.join(model_dir, f'{file_pref}parameters.pkl')
166
+ joblib.dump(dict_params, params_file)
167
+
168
+ class TimeVAE(BaseVariationalAutoencoder):
169
+
170
+ def __init__(self, hidden_layer_sizes, trend_poly=0, num_gen_seas=0, custom_seas=None,
171
+ use_scaler=False, use_residual_conn=True, **kwargs):
172
+ '''
173
+ hidden_layer_sizes: list of number of filters in convolutional layers in encoder and residual connection of decoder.
174
+ trend_poly: integer for number of orders for trend component. e.g. setting trend_poly = 2 will include linear and quadratic term.
175
+ num_gen_seas: Number of sine-waves to use to model seasonalities. Each sine wae will have its own amplitude, frequency and phase.
176
+ custom_seas: list of tuples of (num_seasons, len_per_season).
177
+ num_seasons: number of seasons per cycle.
178
+ len_per_season: number of epochs (time-steps) per season.
179
+ use_residual_conn: boolean value indicating whether to use a residual connection for reconstruction in addition to
180
+ trend, generic and custom seasonalities.
181
+ '''
182
+
183
+ super(TimeVAE, self).__init__(**kwargs)
184
+
185
+ self.hidden_layer_sizes = hidden_layer_sizes
186
+ self.trend_poly = trend_poly
187
+ self.num_gen_seas = num_gen_seas
188
+ self.custom_seas = custom_seas
189
+ self.use_scaler = use_scaler
190
+ self.use_residual_conn = use_residual_conn
191
+ self.encoder = self._get_encoder()
192
+ self.decoder = self._get_decoder()
193
+
194
+ def _get_encoder(self):
195
+ encoder_inputs = Input(shape=(self.seq_len, self.feat_dim), name='encoder_input')
196
+ x = encoder_inputs
197
+ for i, num_filters in enumerate(self.hidden_layer_sizes):
198
+ x = Conv1D(
199
+ filters=num_filters,
200
+ kernel_size=3,
201
+ strides=2,
202
+ activation='relu',
203
+ padding='same',
204
+ name=f'enc_conv_{i}')(x)
205
+
206
+ x = Flatten(name='enc_flatten')(x)
207
+
208
+ # save the dimensionality of this last dense layer before the hidden state layer. We need it in the decoder.
209
+ self.encoder_last_dense_dim = x.get_shape()[-1]
210
+
211
+ z_mean = Dense(self.latent_dim, name="z_mean")(x)
212
+ z_log_var = Dense(self.latent_dim, name="z_log_var")(x)
213
+
214
+ encoder_output = Sampling()([z_mean, z_log_var])
215
+ self.encoder_output = encoder_output
216
+
217
+ encoder = Model(encoder_inputs, [z_mean, z_log_var, encoder_output], name="encoder")
218
+ return encoder
219
+
220
+ def _get_decoder(self):
221
+ decoder_inputs = Input(shape=(int(self.latent_dim)), name='decoder_input')
222
+
223
+ outputs = None
224
+ outputs = self.level_model(decoder_inputs)
225
+
226
+ # trend polynomials
227
+ if self.trend_poly is not None and self.trend_poly > 0:
228
+ trend_vals = self.trend_model(decoder_inputs)
229
+ outputs = trend_vals if outputs is None else outputs + trend_vals
230
+
231
+ # # generic seasonalities
232
+ # if self.num_gen_seas is not None and self.num_gen_seas > 0:
233
+ # gen_seas_vals, freq, phase, amplitude = self.generic_seasonal_model(decoder_inputs)
234
+ # # gen_seas_vals = self.generic_seasonal_model2(decoder_inputs)
235
+ # outputs = gen_seas_vals if outputs is None else outputs + gen_seas_vals
236
+
237
+ # custom seasons
238
+ if self.custom_seas is not None and len(self.custom_seas) > 0:
239
+ cust_seas_vals = self.custom_seasonal_model(decoder_inputs)
240
+ outputs = cust_seas_vals if outputs is None else outputs + cust_seas_vals
241
+
242
+ if self.use_residual_conn:
243
+ residuals = self._get_decoder_residual(decoder_inputs)
244
+ outputs = residuals if outputs is None else outputs + residuals
245
+
246
+ if self.use_scaler and outputs is not None:
247
+ scale = self.scale_model(decoder_inputs)
248
+ outputs *= scale
249
+
250
+ # outputs = Activation(activation='sigmoid')(outputs)
251
+
252
+ if outputs is None:
253
+ raise Exception('''Error: No decoder model to use.
254
+ You must use one or more of:
255
+ trend, generic seasonality(ies), custom seasonality(ies), and/or residual connection. ''')
256
+
257
+ decoder = Model(decoder_inputs, [outputs], name="decoder")
258
+ return decoder
259
+
260
+ def level_model(self, z):
261
+ level_params = Dense(self.feat_dim, name="level_params", activation='relu')(z)
262
+ level_params = Dense(self.feat_dim, name="level_params2")(level_params)
263
+ level_params = Reshape(target_shape=(1, self.feat_dim))(level_params) # shape: (N, 1, D)
264
+
265
+ ones_tensor = tf.ones(shape=[1, self.seq_len, 1], dtype=tf.float32) # shape: (1, T, D)
266
+
267
+ level_vals = level_params * ones_tensor
268
+ return level_vals
269
+
270
+ def scale_model(self, z):
271
+ scale_params = Dense(self.feat_dim, name="scale_params", activation='relu')(z)
272
+ scale_params = Dense(self.feat_dim, name="scale_params2")(scale_params)
273
+ scale_params = Reshape(target_shape=(1, self.feat_dim))(scale_params) # shape: (N, 1, D)
274
+
275
+ scale_vals = tf.repeat(scale_params, repeats=self.seq_len, axis=1) # shape: (N, T, D)
276
+ return scale_vals
277
+
278
+ def trend_model(self, z):
279
+ trend_params = Dense(self.feat_dim * self.trend_poly, name="trend_params", activation='relu')(z)
280
+ trend_params = Dense(self.feat_dim * self.trend_poly, name="trend_params2")(trend_params)
281
+ trend_params = Reshape(target_shape=(self.feat_dim, self.trend_poly))(trend_params) # shape: N x D x P
282
+
283
+ lin_space = K.arange(0, float(self.seq_len), 1) / self.seq_len # shape of lin_space : 1d tensor of length T
284
+ poly_space = K.stack([lin_space ** float(p + 1) for p in range(self.trend_poly)], axis=0) # shape: P x T
285
+
286
+ trend_vals = K.dot(trend_params, poly_space) # shape (N, D, T)
287
+ trend_vals = tf.transpose(trend_vals, perm=[0, 2, 1]) # shape: (N, T, D)
288
+ trend_vals = K.cast(trend_vals, tf.float32)
289
+ return trend_vals
290
+
291
+ def custom_seasonal_model(self, z):
292
+
293
+ N = tf.shape(z)[0]
294
+ ones_tensor = tf.ones(shape=[N, self.feat_dim, self.seq_len], dtype=tf.int32)
295
+
296
+ all_seas_vals = []
297
+ for i, season_tup in enumerate(self.custom_seas):
298
+ num_seasons, len_per_season = season_tup
299
+
300
+ season_params = Dense(self.feat_dim * num_seasons, name=f"season_params_{i}")(z) # shape: (N, D * S)
301
+ season_params = Reshape(target_shape=(self.feat_dim, num_seasons))(season_params) # shape: (N, D, S)
302
+
303
+ season_indexes_over_time = self._get_season_indexes_over_seq(num_seasons, len_per_season) # shape: (T, )
304
+
305
+ dim2_idxes = ones_tensor * tf.reshape(season_indexes_over_time, shape=(1, 1, -1)) # shape: (1, 1, T)
306
+
307
+ season_vals = tf.gather(season_params, dim2_idxes, batch_dims=-1) # shape (N, D, T)
308
+
309
+ all_seas_vals.append(season_vals)
310
+
311
+ all_seas_vals = K.stack(all_seas_vals, axis=-1) # shape: (N, D, T, S)
312
+ all_seas_vals = tf.reduce_sum(all_seas_vals, axis=-1) # shape (N, D, T)
313
+ all_seas_vals = tf.transpose(all_seas_vals, perm=[0, 2, 1]) # shape (N, T, D)
314
+ return all_seas_vals
315
+
316
+ def _get_season_indexes_over_seq(self, num_seasons, len_per_season):
317
+ curr_len = 0
318
+ season_idx = []
319
+ curr_idx = 0
320
+ while curr_len < self.seq_len:
321
+ reps = len_per_season if curr_len + len_per_season <= self.seq_len else self.seq_len - curr_len
322
+ season_idx.extend([curr_idx] * reps)
323
+ curr_idx += 1
324
+ if curr_idx == num_seasons: curr_idx = 0
325
+ curr_len += reps
326
+ return season_idx
327
+
328
+ def generic_seasonal_model(self, z):
329
+
330
+ freq = Dense(self.feat_dim * self.num_gen_seas, name="g_season_freq", activation='sigmoid')(z)
331
+ freq = Reshape(target_shape=(1, self.feat_dim, self.num_gen_seas))(freq) # shape: (N, 1, D, S)
332
+
333
+ phase = Dense(self.feat_dim * self.num_gen_seas, name="g_season_phase")(z)
334
+ phase = Reshape(target_shape=(1, self.feat_dim, self.num_gen_seas))(phase) # shape: (N, 1, D, S)
335
+
336
+ amplitude = Dense(self.feat_dim * self.num_gen_seas, name="g_season_amplitude")(z)
337
+ amplitude = Reshape(target_shape=(1, self.feat_dim, self.num_gen_seas))(amplitude) # shape: (N, 1, D, S)
338
+
339
+ lin_space = K.arange(0, float(self.seq_len), 1) / self.seq_len # shape of lin_space : 1d tensor of length T
340
+ lin_space = tf.reshape(lin_space, shape=(1, self.seq_len, 1, 1)) # shape: 1, T, 1, 1
341
+
342
+ seas_vals = amplitude * K.sin(2. * np.pi * freq * lin_space + phase) # shape: N, T, D, S
343
+ seas_vals = tf.math.reduce_sum(seas_vals, axis=-1) # shape: N, T, D
344
+
345
+ return seas_vals
346
+
347
+ def generic_seasonal_model2(self, z):
348
+
349
+ season_params = Dense(self.feat_dim * self.num_gen_seas, name="g_season_params")(z)
350
+ season_params = Reshape(target_shape=(self.feat_dim, self.num_gen_seas))(season_params) # shape: (D, S)
351
+
352
+ p = self.num_gen_seas
353
+ p1, p2 = (p // 2, p // 2) if p % 2 == 0 else (p // 2, p // 2 + 1)
354
+
355
+ ls = K.arange(0, float(self.seq_len), 1) / self.seq_len # shape of ls : 1d tensor of length T
356
+
357
+ s1 = K.stack([K.cos(2 * np.pi * i * ls) for i in range(p1)], axis=0)
358
+ s2 = K.stack([K.sin(2 * np.pi * i * ls) for i in range(p2)], axis=0)
359
+ if p == 1:
360
+ s = s2
361
+ else:
362
+ s = K.concatenate([s1, s2], axis=0)
363
+ s = K.cast(s, np.float32)
364
+
365
+ seas_vals = K.dot(season_params, s, name='g_seasonal_vals')
366
+ seas_vals = tf.transpose(seas_vals, perm=[0, 2, 1]) # shape: (N, T, D)
367
+ seas_vals = K.cast(seas_vals, np.float32)
368
+ print('seas_vals shape', tf.shape(seas_vals))
369
+
370
+ return seas_vals
371
+
372
+ def _get_decoder_residual(self, x):
373
+
374
+ x = Dense(self.encoder_last_dense_dim, name="dec_dense", activation='relu')(x)
375
+ x = Reshape(target_shape=(-1, self.hidden_layer_sizes[-1]), name="dec_reshape")(x)
376
+
377
+ for i, num_filters in enumerate(reversed(self.hidden_layer_sizes[:-1])):
378
+ x = Conv1DTranspose(
379
+ filters=num_filters,
380
+ kernel_size=3,
381
+ strides=2,
382
+ padding='same',
383
+ activation='relu',
384
+ name=f'dec_deconv_{i}')(x)
385
+
386
+ # last de-convolution
387
+ x = Conv1DTranspose(
388
+ filters=self.feat_dim,
389
+ kernel_size=3,
390
+ strides=2,
391
+ padding='same',
392
+ activation='relu',
393
+ name=f'dec_deconv__{i + 1}')(x)
394
+
395
+ x = Flatten(name='dec_flatten')(x)
396
+ x = Dense(self.seq_len * self.feat_dim, name="decoder_dense_final")(x)
397
+ residuals = Reshape(target_shape=(self.seq_len, self.feat_dim))(x)
398
+ return residuals
399
+
400
+ def save(self, model_dir, file_pref):
401
+
402
+ super().save_weights(model_dir, file_pref)
403
+ dict_params = {
404
+ 'seq_len': self.seq_len,
405
+ 'feat_dim': self.feat_dim,
406
+ 'latent_dim': self.latent_dim,
407
+ 'reconstruction_wt': self.reconstruction_wt,
408
+
409
+ 'hidden_layer_sizes': self.hidden_layer_sizes,
410
+ 'trend_poly': self.trend_poly,
411
+ 'num_gen_seas': self.num_gen_seas,
412
+ 'custom_seas': self.custom_seas,
413
+ 'use_scaler': self.use_scaler,
414
+ 'use_residual_conn': self.use_residual_conn,
415
+ }
416
+ params_file = os.path.join(model_dir, f'{file_pref}parameters.pkl')
417
+ joblib.dump(dict_params, params_file)
418
+
419
+ @staticmethod
420
+ def load(model_dir, file_pref):
421
+ params_file = os.path.join(model_dir, f'{file_pref}parameters.pkl')
422
+ dict_params = joblib.load(params_file)
423
+
424
+ vae_model = TimeVAE(**dict_params)
425
+
426
+ vae_model.load_weights(model_dir, file_pref)
427
+
428
+ vae_model.compile(optimizer=Adam())
429
+
430
+ return vae_model
GAN/utils.py ADDED
@@ -0,0 +1,315 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ import numpy as np
3
+ import typing as T
4
+ import seaborn as sns
5
+ import matplotlib.pyplot as plt
6
+ import sklearn
7
+ import sklearn.manifold
8
+ import tensorflow as tf
9
+ import numpy.typing as npt
10
+
11
+ from tensorflow import keras
12
+ from tensorflow.python.types.core import TensorLike
13
+
14
+ Tensor = T.Union[tf.Tensor, npt.NDArray]
15
+ OptTensor = T.Optional[Tensor]
16
+
17
+
18
+ EPS = 1e-18
19
+ class TSFeatureScaler:
20
+ """Global time series scaler that scales all features to [0,1] then normalizes to [-1,1]"""
21
+
22
+ def __init__(self) -> None:
23
+ self.min_val = None
24
+ self.max_val = None
25
+
26
+ def fit(self, X: TensorLike) -> "TSFeatureScaler":
27
+ """
28
+ Fit scaler to data
29
+
30
+ Args:
31
+ X: Input tensor of shape [N, T, D]
32
+ (N: samples, T: timesteps, D: features)
33
+ """
34
+ # 计算整个数据集的全局最大最小值
35
+ self.min_val = np.min(X)
36
+ self.max_val = np.max(X)
37
+ return self
38
+
39
+ def transform(self, X: TensorLike) -> TensorLike:
40
+ """
41
+ Transform data in two steps:
42
+ 1. Scale to [0,1] using min-max scaling
43
+ 2. Normalize to [-1,1]
44
+ """
45
+ if self.min_val is None or self.max_val is None:
46
+ raise ValueError("Scaler must be fitted before transform")
47
+
48
+ # 1. 缩放到[0,1]
49
+ X_scaled = (X - self.min_val) / (self.max_val - self.min_val + EPS)
50
+
51
+ # 2. 归一化到[-1,1]
52
+ X_normalized = 2.0 * X_scaled - 1.0
53
+
54
+ return X_normalized
55
+
56
+ def inverse_transform(self, X: TensorLike) -> TensorLike:
57
+ """
58
+ Inverse transform data:
59
+ 1. From [-1,1] back to [0,1]
60
+ 2. From [0,1] back to original range
61
+ """
62
+ if self.min_val is None or self.max_val is None:
63
+ raise ValueError("Scaler must be fitted before inverse_transform")
64
+
65
+ # 1. 从[-1,1]转回[0,1]
66
+ X_scaled = (X + 1.0) / 2.0
67
+
68
+ # 2. 从[0,1]转回原始范围
69
+ X_original = X_scaled * (self.max_val - self.min_val + EPS) + self.min_val
70
+
71
+ return X_original
72
+
73
+ def fit_transform(self, X: TensorLike) -> TensorLike:
74
+ """Fit to data, then transform it"""
75
+ return self.fit(X).transform(X)
76
+
77
+ def get_range(self) -> T.Tuple[float, float]:
78
+ """获取原始数据的范围"""
79
+ if self.min_val is None or self.max_val is None:
80
+ raise ValueError("Scaler must be fitted first")
81
+ return (self.min_val, self.max_val)
82
+
83
+
84
+ EPS = 1e-18
85
+ class TSFeatureWiseScaler():
86
+ def __init__(self, feature_range: T.Tuple[float, float] = (0, 1)) -> None:
87
+ assert len(feature_range) == 2
88
+
89
+ self._min_v, self._max_v = feature_range
90
+
91
+ # X: N x T x D
92
+ def fit(self, X: TensorLike) -> "TSFeatureWiseScaler":
93
+ D = X.shape[2]
94
+ self.mins = np.zeros(D)
95
+ self.maxs = np.zeros(D)
96
+
97
+ for i in range(D):
98
+ self.mins[i] = np.min(X[:, :, i])
99
+ self.maxs[i] = np.max(X[:, :, i])
100
+
101
+ return self
102
+
103
+ def transform(self, X: TensorLike) -> TensorLike:
104
+ return ((X - self.mins) / (self.maxs - self.mins + EPS)) * (self._max_v - self._min_v) + self._min_v
105
+
106
+ def inverse_transform(self, X: TensorLike) -> TensorLike:
107
+ X -= self._min_v
108
+ X /= self._max_v - self._min_v
109
+ X *= (self.maxs - self.mins + EPS)
110
+ X += self.mins
111
+ return X
112
+
113
+ def fit_transform(self, X: TensorLike) -> TensorLike:
114
+ self.fit(X)
115
+ return self.transform(X)
116
+
117
+
118
+ def linear_beta_schedule(timesteps, beta_start=1e-4, beta_end=0.99): # beta_end=0.99
119
+ betas = np.linspace(beta_start, beta_end, timesteps, dtype=np.float32)
120
+ return betas
121
+
122
+
123
+ def cosine_beta_schedule(timesteps, s=0.008):
124
+ steps = timesteps + 1
125
+ x = np.linspace(0, timesteps, steps, dtype=np.float64)
126
+ alphas_cumprod = np.cos(((x / timesteps) + s) / (1 + s) * math.pi * 0.5) ** 2
127
+ alphas_cumprod = alphas_cumprod / alphas_cumprod[0]
128
+ betas = 1 - (alphas_cumprod[1:] / alphas_cumprod[:-1])
129
+ betas = np.clip(betas, 0, 0.999)
130
+ return betas
131
+
132
+
133
+ def reconstruction_loss_by_axis(original: tf.Tensor, reconstructed: tf.Tensor, axis: int = 0) -> tf.Tensor:
134
+ """
135
+ Calculate the reconstruction loss based on a specified axis.
136
+
137
+ This function computes the reconstruction loss between the original data and
138
+ the reconstructed data along a specified axis. The loss can be computed in
139
+ two ways depending on the chosen axis:
140
+
141
+ - When `axis` is 0, it computes the loss as the sum of squared differences
142
+ between the original and reconstructed data for all elements.
143
+ - When `axis` is 1 or 2, it computes the mean squared error (MSE) between the
144
+ mean values along the chosen axis for the original and reconstructed data.
145
+
146
+ Parameters:
147
+ ----------
148
+ original : tf.Tensor
149
+ The original data tensor.
150
+
151
+ reconstructed : tf.Tensor
152
+ The reconstructed data tensor, typically produced by an autoencoder.
153
+
154
+ axis : int, optional (default=0)
155
+ The axis along which to compute the reconstruction loss:
156
+ - 0: All elements (sum of squared differences).
157
+ - 1: Along features (MSE).
158
+ - 2: Along time steps (MSE).
159
+
160
+ Returns:
161
+ -------
162
+ tf.Tensor
163
+ The computed reconstruction loss as a TensorFlow tensor.
164
+ Notes:
165
+ ------
166
+ - This function is commonly used in the context of autoencoders and other
167
+ reconstruction-based models to assess the quality of the reconstruction.
168
+ - The choice of `axis` determines how the loss is calculated, and it should
169
+ align with the data's structure.
170
+ """
171
+
172
+ # axis=0 all (sum of squared diffs)
173
+ # axis=1 features (MSE)
174
+ # axis=2 times (MSE)
175
+ if axis == 0:
176
+ return tf.reduce_sum(tf.math.squared_difference(original, reconstructed))
177
+ else:
178
+ return tf.losses.mean_squared_error(tf.reduce_mean(original, axis=axis), tf.reduce_mean(reconstructed, axis=axis))
179
+
180
+
181
+ def gen_sine_dataset(N: int, T: int, D: int, max_value: int = 10) -> npt.NDArray:
182
+ result = []
183
+ for i in range(N):
184
+ result.append([])
185
+ a = np.random.random() * max_value
186
+ shift = np.random.random() * max_value + 1
187
+ ts = np.arange(0, T, 1)
188
+ for d in range(1, D + 1):
189
+ result[-1].append((a * np.sin((d + 3) * ts / 25. + shift)).T)
190
+
191
+ return np.transpose(np.array(result), [0, 2, 1])
192
+
193
+
194
+ def gen_sine_vs_const_dataset(N: int, T: int, D: int, max_value: int = 10, const: int = 0) -> T.Tuple[TensorLike, TensorLike]:
195
+ result_X, result_y = [], []
196
+ for i in range(N):
197
+ scales = np.random.random(D) * max_value
198
+ consts = np.random.random(D) * const
199
+ shifts = np.random.random(D) * 2
200
+ alpha = np.random.random()
201
+ if np.random.random() < 0.5:
202
+ times = np.repeat(np.arange(0, T, 1)[:, None], D, axis=1) / 10
203
+ result_X.append(np.sin(alpha * times + shifts) * scales)
204
+ result_y.append(0)
205
+ else:
206
+ result_X.append(np.tile(consts, (T, 1)))
207
+ result_y.append(1)
208
+ return np.array(result_X), np.array(result_y)
209
+
210
+
211
+ def visualize_ts_lineplot(
212
+ ts: Tensor,
213
+ ys: OptTensor = None,
214
+ num: int = 5,
215
+ unite_features: bool = True,
216
+ ) -> None:
217
+ assert len(ts.shape) == 3
218
+
219
+ fig, axs = plt.subplots(num, 1, figsize=(14, 10))
220
+ if num == 1:
221
+ axs = [axs]
222
+
223
+ ids = np.random.choice(ts.shape[0], size=num, replace=False)
224
+ for i, sample_id in enumerate(ids):
225
+ if not unite_features:
226
+ feature_id = np.random.randint(ts.shape[2])
227
+ sns.lineplot(
228
+ x=range(ts.shape[1]),
229
+ y=ts[sample_id, :, feature_id],
230
+ ax=axs[i],
231
+ label=rf"feature \#{feature_id}",
232
+ )
233
+ else:
234
+ for feat_id in range(ts.shape[2]):
235
+ sns.lineplot(
236
+ x=range(ts.shape[1]), y=ts[sample_id, :, feat_id], ax=axs[i]
237
+ )
238
+ if ys is not None:
239
+ if len(ys.shape) == 1:
240
+ axs[i].set_title(ys[sample_id])
241
+ elif len(ys.shape) == 2:
242
+ sns.lineplot(
243
+ x=range(ts.shape[1]),
244
+ y=ys[sample_id],
245
+ ax=axs[i].twinx(),
246
+ color="g",
247
+ label="Target variable",
248
+ )
249
+ else:
250
+ raise ValueError("ys contains too many dimensions")
251
+ #plt.show()
252
+
253
+ def visualize_tsne(
254
+ X: Tensor,
255
+ y: Tensor,
256
+ X_gen: Tensor,
257
+ y_gen: Tensor,
258
+ path: str = "/tmp/tsne_embeddings.pdf",
259
+ feature_averaging: bool = False,
260
+ perplexity=30.0
261
+ ) -> None:
262
+ """
263
+ Visualizes t-SNE embeddings of real and synthetic data.
264
+
265
+ This function generates a scatter plot of t-SNE embeddings for real and synthetic data.
266
+ Each data point is represented by a marker on the plot, and the colors of the markers
267
+ correspond to the corresponding class labels of the data points.
268
+
269
+ :param X: The original real data tensor of shape (num_samples, num_features).
270
+ :type X: tsgm.types.Tensor
271
+ :param y: The labels of the original real data tensor of shape (num_samples,).
272
+ :type y: tsgm.types.Tensor
273
+ :param X_gen: The generated synthetic data tensor of shape (num_samples, num_features).
274
+ :type X_gen: tsgm.types.Tensor
275
+ :param y_gen: The labels of the generated synthetic data tensor of shape (num_samples,).
276
+ :type y_gen: tsgm.types.Tensor
277
+ :param path: The path to save the visualization as a PDF file. Defaults to "/tmp/tsne_embeddings.pdf".
278
+ :type path: str, optional
279
+ :param feature_averaging: Whether to compute the average features for each class. Defaults to False.
280
+ :type feature_averaging: bool, optional
281
+ """
282
+ tsne = sklearn.manifold.TSNE(n_components=2, perplexity=perplexity, learning_rate="auto", init="random")
283
+
284
+ if feature_averaging:
285
+ X_all = np.concatenate((np.mean(X, axis=2), np.mean(X_gen, axis=2)))
286
+
287
+ X_emb = tsne.fit_transform(np.resize(X_all, (X_all.shape[0], X_all.shape[1])))
288
+ else:
289
+ X_all = np.concatenate((X, X_gen))
290
+
291
+ X_emb = tsne.fit_transform(
292
+ np.resize(X_all, (X_all.shape[0], X_all.shape[1] * X_all.shape[2]))
293
+ )
294
+
295
+ y_all = np.concatenate((y, y_gen))
296
+
297
+ c = np.argmax(y_all, axis=1)
298
+ colors = {0: "class 0", 1: "class 1"}
299
+ c = [colors[el] for el in c]
300
+ point_styles = ["hist"] * X.shape[0] + ["gen"] * X_gen.shape[0]
301
+
302
+ plt.figure(figsize=(8, 6), dpi=80)
303
+ sns.scatterplot(
304
+ x=X_emb[:, 0],
305
+ y=X_emb[:, 1],
306
+ hue=c,
307
+ style=point_styles,
308
+ markers={"hist": "<", "gen": "H"},
309
+ alpha=0.7,
310
+ )
311
+ plt.legend()
312
+ plt.box(False)
313
+ plt.axis("off")
314
+ plt.savefig(path)
315
+ plt.show()
GAN/zoo.py ADDED
@@ -0,0 +1,517 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import abc
2
+ import math
3
+ import typing as T
4
+ import tensorflow as tf
5
+ import numpy.typing as npt
6
+ from tensorflow import keras
7
+ from tensorflow.keras import layers
8
+
9
+ from prettytable import PrettyTable
10
+
11
+ Tensor = T.Union[tf.Tensor, npt.NDArray]
12
+ OptTensor = T.Optional[Tensor]
13
+
14
+ class Sampling(tf.keras.layers.Layer):
15
+ def call(self, inputs: Tensor) -> Tensor:
16
+ z_mean, z_log_var = inputs
17
+ epsilon = tf.keras.backend.random_normal(shape=tf.shape(z_mean))
18
+ return z_mean + tf.exp(0.5 * z_log_var) * epsilon
19
+
20
+
21
+ class Architecture(abc.ABC):
22
+ @abc.abstractproperty
23
+ def arch_type(self):
24
+ raise NotImplementedError
25
+
26
+ class BaseGANArchitecture(Architecture):
27
+ @property
28
+ def discriminator(self) -> keras.models.Model:
29
+ if hasattr(self, "_discriminator"):
30
+ return self._discriminator
31
+ else:
32
+ raise NotImplementedError
33
+
34
+ @property
35
+ def generator(self) -> keras.models.Model:
36
+ if hasattr(self, "_generator"):
37
+ return self._generator
38
+ else:
39
+ raise NotImplementedError
40
+
41
+ def get(self) -> T.Dict:
42
+ if hasattr(self, "_discriminator") and hasattr(self, "_generator"):
43
+ return {"discriminator": self._discriminator, "generator": self._generator}
44
+ else:
45
+ raise NotImplementedError
46
+
47
+ class BaseVAEArchitecture(Architecture):
48
+ @property
49
+ def encoder(self) -> keras.models.Model:
50
+ if hasattr(self, "_encoder"):
51
+ return self._encoder
52
+ else:
53
+ raise NotImplementedError
54
+
55
+ @property
56
+ def decoder(self) -> keras.models.Model:
57
+ if hasattr(self, "_decoder"):
58
+ return self._decoder
59
+ else:
60
+ raise NotImplementedError
61
+
62
+ def get(self) -> T.Dict:
63
+ if hasattr(self, "_encoder") and hasattr(self, "_decoder"):
64
+ return {"encoder": self._encoder, "decoder": self._decoder}
65
+ else:
66
+ raise NotImplementedError
67
+
68
+ class VAE_CONV5Architecture(BaseVAEArchitecture):
69
+ arch_type = "vae: conv"
70
+
71
+ def __init__(self, seq_len: int, feat_dim: int, latent_dim: int) -> None:
72
+ super().__init__()
73
+ self._seq_len = seq_len
74
+ self._feat_dim = feat_dim
75
+ self._latent_dim = latent_dim
76
+ self._encoder = self._build_encoder()
77
+ self._decoder = self._build_decoder()
78
+
79
+ def _build_encoder(self) -> keras.models.Model:
80
+ encoder_inputs = keras.Input(shape=(self._seq_len, self._feat_dim))
81
+ x = layers.Conv1D(64, 3, activation="relu", strides=1, padding="same")(
82
+ encoder_inputs
83
+ )
84
+ x = layers.Dropout(rate=0.2)(x)
85
+ # x = layers.Conv1D(64, 2, activation="relu", strides=1, padding="same")(x)
86
+ # x = layers.Dropout(rate=0.2)(x)
87
+ # x = layers.Conv1D(64, 2, activation="relu", strides=1, padding="same")(x)
88
+ # x = layers.Dropout(rate=0.2)(x)
89
+ # x = layers.Conv1D(64, 2, activation="relu", strides=1, padding="same")(x)
90
+ # x = layers.Dropout(rate=0.2)(x)
91
+ # x = layers.Conv1D(64, 4, activation="relu", strides=1, padding="same")(x)
92
+ # x = layers.Dropout(rate=0.2)(x)
93
+ x = layers.Flatten()(x)
94
+ #x = layers.Dense(512, activation="relu")(x)
95
+ x = layers.Dense(64, activation="relu")(x)
96
+ z_mean = layers.Dense(self._latent_dim, name="z_mean")(x)
97
+ z_log_var = layers.Dense(self._latent_dim, name="z_log_var")(x)
98
+ z = Sampling()([z_mean, z_log_var])
99
+ encoder = keras.Model(encoder_inputs, [z_mean, z_log_var, z], name="encoder")
100
+ return encoder
101
+
102
+ def _build_decoder(self) -> keras.models.Model:
103
+ latent_inputs = keras.Input(shape=(self._latent_dim,))
104
+ x = layers.Dense(64, activation="relu")(latent_inputs)
105
+ # x = layers.Dense(512, activation="relu")(x)
106
+ # x = layers.Dense(64, activation="relu")(x)
107
+
108
+ dense_shape = self._encoder.layers[-6].output_shape[1] * self._seq_len
109
+
110
+ x = layers.Dense(dense_shape, activation="relu")(x)
111
+
112
+ x = layers.Reshape((self._seq_len, dense_shape // self._seq_len))(x)
113
+ x = layers.Conv1DTranspose(64, 2, activation="relu", strides=1, padding="same")(
114
+ x
115
+ )
116
+ x = layers.Dropout(rate=0.2)(x)
117
+ # x = layers.Conv1DTranspose(64, 2, activation="relu", strides=1, padding="same")(
118
+ # x
119
+ # )
120
+ # x = layers.Dropout(rate=0.2)(x)
121
+ # x = layers.Conv1DTranspose(64, 2, activation="relu", strides=1, padding="same")(
122
+ # x
123
+ # )
124
+ # x = layers.Dropout(rate=0.2)(x)
125
+ # x = layers.Conv1DTranspose(64, 2, activation="relu", strides=1, padding="same")(
126
+ # x
127
+ # )
128
+ # x = layers.Dropout(rate=0.2)(x)
129
+ # x = layers.Conv1DTranspose(
130
+ # 64, 10, activation="relu", strides=1, padding="same"
131
+ # )(x)
132
+ # x = layers.Dropout(rate=0.2)(x)
133
+ decoder_outputs = layers.Conv1DTranspose(
134
+ self._feat_dim, 3, activation="sigmoid", padding="same"
135
+ )(x)
136
+ decoder = keras.Model(latent_inputs, decoder_outputs, name="decoder")
137
+ return decoder
138
+
139
+ class cVAE_CONV5Architecture(BaseVAEArchitecture):
140
+ arch_type = "vae:conditional"
141
+
142
+ def __init__(self, seq_len: int, feat_dim: int, latent_dim: int, output_dim: int = 2) -> None:
143
+ self._seq_len = seq_len
144
+ self._feat_dim = feat_dim
145
+ self._latent_dim = latent_dim
146
+ self._output_dim = output_dim
147
+
148
+ self._encoder = self._build_encoder()
149
+ self._decoder = self._build_decoder()
150
+
151
+ def _build_encoder(self) -> keras.models.Model:
152
+ encoder_inputs = keras.Input(
153
+ shape=(self._seq_len, self._feat_dim + self._output_dim)
154
+ )
155
+
156
+ x = layers.Conv1D(64, 3, activation="relu", strides=1, padding="same")(
157
+ encoder_inputs
158
+ )
159
+ x = layers.Dropout(rate=0.2)(x)
160
+ # x = layers.Conv1D(64, 2, activation="relu", strides=1, padding="same")(x)
161
+ # x = layers.Dropout(rate=0.2)(x)
162
+ # x = layers.Conv1D(64, 2, activation="relu", strides=1, padding="same")(x)
163
+ # x = layers.Dropout(rate=0.2)(x)
164
+ # x = layers.Conv1D(64, 2, activation="relu", strides=1, padding="same")(x)
165
+ # x = layers.Dropout(rate=0.2)(x)
166
+ # x = layers.Conv1D(64, 4, activation="relu", strides=1, padding="same")(x)
167
+ # x = layers.Dropout(rate=0.2)(x)
168
+ x = layers.Flatten()(x)
169
+ #x = layers.Dense(512, activation="relu")(x)
170
+ x = layers.Dense(64, activation="relu")(x)
171
+ z_mean = layers.Dense(self._latent_dim * self._seq_len, name="z_mean")(x)
172
+ z_log_var = layers.Dense(self._latent_dim * self._seq_len, name="z_log_var")(x)
173
+ z = Sampling()([z_mean, z_log_var])
174
+ encoder = keras.Model(encoder_inputs, [z_mean, z_log_var, z], name="encoder")
175
+ return encoder
176
+
177
+ def _build_decoder(self) -> keras.models.Model:
178
+ inputs = keras.Input(
179
+ shape=(
180
+ self._seq_len,
181
+ self._latent_dim + self._output_dim,
182
+ )
183
+ )
184
+ x = layers.Conv1DTranspose(64, 2, strides=2, padding="same")(inputs)
185
+ x = layers.LeakyReLU(alpha=0.2)(x)
186
+ x = layers.Dropout(rate=0.2)(x)
187
+ # x = layers.Conv1DTranspose(64, 2, strides=2, padding="same")(x)
188
+ # x = layers.LeakyReLU(alpha=0.2)(x)
189
+ # x = layers.Dropout(rate=0.2)(x)
190
+ # x = layers.Conv1DTranspose(64, 2, strides=2, padding="same")(x)
191
+ # x = layers.LeakyReLU(alpha=0.2)(x)
192
+ # x = layers.Dropout(rate=0.2)(x)
193
+
194
+ pool_and_stride = round((x.shape[1] + 1) / (self._seq_len + 1))
195
+ x = layers.AveragePooling1D(pool_size=pool_and_stride, strides=pool_and_stride)(
196
+ x
197
+ )
198
+ d_output = layers.LocallyConnected1D(self._feat_dim, 1, activation="sigmoid")(x)
199
+
200
+ decoder = keras.Model(inputs, d_output, name="decoder")
201
+ return decoder
202
+ class GAN_ConvLSTM4Architecture(BaseGANArchitecture):
203
+ arch_type = "gan: conv_lstm"
204
+
205
+ def __init__(self, seq_len: int, feat_dim: int, latent_dim: int, output_dim: int) -> None:
206
+ super().__init__()
207
+ self._seq_len = seq_len
208
+ self._feat_dim = feat_dim
209
+ self._latent_dim = latent_dim
210
+ self._output_dim = output_dim
211
+ self.generator_in_channels = latent_dim + output_dim
212
+ self.discriminator_in_channels = feat_dim + output_dim
213
+
214
+ self._discriminator = self._build_discriminator()
215
+ self._generator = self._build_generator()
216
+
217
+ def _build_discriminator(self) -> keras.models.Model:
218
+ d_input = keras.Input((self._seq_len, self.discriminator_in_channels))
219
+ x = layers.Conv1D(64, 3, strides=2, padding="same")(d_input)
220
+ x = layers.LeakyReLU(alpha=0.2)(x)
221
+ x = layers.Dropout(rate=0.2)(x)
222
+ # x = layers.Conv1D(128, 3, strides=2, padding="same")(x)
223
+ # x = layers.LeakyReLU(alpha=0.2)(x)
224
+ # x = layers.Dropout(rate=0.2)(x)
225
+ # x = layers.Conv1D(128, 3, strides=2, padding="same")(x)
226
+ # x = layers.LeakyReLU(alpha=0.2)(x)
227
+ # x = layers.Dropout(rate=0.2)(x)
228
+ # x = layers.Conv1D(128, 3, strides=2, padding="same")(x)
229
+ # x = layers.LeakyReLU(alpha=0.2)(x)
230
+ # x = layers.Dropout(rate=0.2)(x)
231
+ x = layers.GlobalAvgPool1D()(x)
232
+ d_output = layers.Dense(1, activation="sigmoid")(x)
233
+ discriminator = keras.Model(d_input, d_output, name="discriminator")
234
+ return discriminator
235
+
236
+ def _build_generator(self) -> keras.models.Model:
237
+ g_input = keras.Input((self.generator_in_channels,))
238
+ x = layers.Dense(8 * 8 * self._seq_len)(g_input)
239
+ x = layers.LeakyReLU(alpha=0.2)(x)
240
+ x = layers.Reshape((self._seq_len, 64))(x)
241
+ x = layers.Conv1DTranspose(128, 4, strides=2, padding="same")(x)
242
+ x = layers.LeakyReLU(alpha=0.2)(x)
243
+ # x = layers.Conv1DTranspose(128, 4, strides=2, padding="same")(x)
244
+ # x = layers.LeakyReLU(alpha=0.2)(x)
245
+ # x = layers.Conv1DTranspose(128, 4, strides=2, padding="same")(x)
246
+ # x = layers.LeakyReLU(alpha=0.2)(x)
247
+ # x = layers.Conv1DTranspose(128, 4, strides=2, padding="same")(x)
248
+ # x = layers.LeakyReLU(alpha=0.2)(x)
249
+ x = layers.Conv1D(1, 8, padding="same")(x)
250
+ x = layers.LSTM(256, return_sequences=True)(x)
251
+
252
+ pool_and_stride = math.ceil((x.shape[1] + 1) / (self._seq_len + 1))
253
+
254
+ x = layers.AveragePooling1D(pool_size=pool_and_stride, strides=pool_and_stride)(
255
+ x
256
+ )
257
+ g_output = layers.LocallyConnected1D(self._feat_dim, 1, activation="tanh")(x)
258
+ generator = keras.Model(g_input, g_output, name="generator")
259
+ return generator
260
+
261
+
262
+
263
+ class GAN_Conv2LSTM4Architecture(BaseGANArchitecture):
264
+ arch_type = "gan: conv_lstm_2"
265
+
266
+ def __init__(self, seq_len: int, feat_dim: int, latent_dim: int, output_dim: int, n_blocks: int = 1, output_activation: str = "tanh") -> None:
267
+ super().__init__()
268
+ self._seq_len = seq_len
269
+ self._feat_dim = feat_dim
270
+ self._latent_dim = latent_dim
271
+ self._output_dim = output_dim
272
+ self._n_blocks = n_blocks
273
+ self._output_activation = output_activation
274
+
275
+ self.generator_in_channels = latent_dim + output_dim
276
+ self.discriminator_in_channels = feat_dim + output_dim
277
+
278
+ self._discriminator = self._build_discriminator()
279
+ self._generator = self._build_generator(output_activation=output_activation)
280
+
281
+ def _build_discriminator(self) -> keras.Model:
282
+ d_input = keras.Input((self._seq_len, self.discriminator_in_channels))
283
+ x = d_input
284
+ for i in range(self._n_blocks - 1):
285
+ x = layers.LSTM(64, return_sequences=True)(x)
286
+ x = layers.Dropout(rate=0.2)(x)
287
+
288
+ x = layers.LSTM(64, return_sequences=True)(x)
289
+ x = layers.Dropout(rate=0.2)(x)
290
+
291
+ x = layers.GlobalAvgPool1D()(x)
292
+ d_output = layers.Dense(1, activation="sigmoid")(x)
293
+ discriminator = keras.Model(d_input, d_output, name="discriminator")
294
+ return discriminator
295
+
296
+ def _build_generator(self, output_activation: str) -> keras.Model:
297
+ g_input = keras.Input((self.generator_in_channels,))
298
+
299
+ x = layers.Dense(8 * 8 * self._seq_len)(g_input)
300
+ x = layers.LeakyReLU(alpha=0.2)(x)
301
+ x = layers.Reshape((self._seq_len, 64))(x)
302
+
303
+ for i in range(self._n_blocks - 1):
304
+ x = layers.LSTM(64, return_sequences=True)(x)
305
+ x = layers.Dropout(rate=0.2)(x)
306
+ x = layers.LSTM(256, return_sequences=True)(x)
307
+
308
+ pool_and_stride = round((x.shape[1] + 1) / (self._seq_len + 1))
309
+
310
+ x = layers.AveragePooling1D(pool_size=pool_and_stride, strides=pool_and_stride)(x)
311
+ g_output = layers.LocallyConnected1D(self._feat_dim, 1, activation=output_activation)(x)
312
+ generator = keras.Model(g_input, g_output, name="generator")
313
+ return generator
314
+
315
+ class GAN_Conv3LSTM4Architecture(BaseGANArchitecture):
316
+ arch_type = "gan: conv_lstm_3"
317
+
318
+ def __init__(self, seq_len: int, feat_dim: int, latent_dim: int, output_dim: int) -> None:
319
+ super().__init__()
320
+ self._seq_len = seq_len
321
+ self._feat_dim = feat_dim
322
+ self._latent_dim = latent_dim
323
+ self._output_dim = output_dim
324
+
325
+ self.generator_in_channels = latent_dim + output_dim
326
+ self.discriminator_in_channels = feat_dim + output_dim
327
+
328
+ self._discriminator = self._build_discriminator()
329
+ self._generator = self._build_generator()
330
+
331
+ def _build_discriminator(self) -> keras.models.Model:
332
+ d_input = keras.Input((self._seq_len, self.discriminator_in_channels))
333
+ x = layers.LSTM(64, return_sequences=True)(d_input)
334
+ x = layers.LeakyReLU(alpha=0.2)(x)
335
+ x = layers.Dropout(rate=0.2)(x)
336
+ x = layers.Conv1D(128, 3, strides=2, padding="same")(x)
337
+ x = layers.LeakyReLU(alpha=0.2)(x)
338
+ x = layers.Dropout(rate=0.2)(x)
339
+ x = layers.Conv1D(128, 3, strides=2, padding="same")(x)
340
+ x = layers.LeakyReLU(alpha=0.2)(x)
341
+ x = layers.Dropout(rate=0.2)(x)
342
+ x = layers.Conv1D(128, 3, strides=2, padding="same")(x)
343
+ x = layers.LeakyReLU(alpha=0.2)(x)
344
+ x = layers.Dropout(rate=0.2)(x)
345
+ x = layers.GlobalAvgPool1D()(x)
346
+ d_output = layers.Dense(1, activation="sigmoid")(x)
347
+ discriminator = keras.Model(d_input, d_output, name="discriminator")
348
+ return discriminator
349
+
350
+ def _build_generator(self) -> keras.models.Model:
351
+ g_input = keras.Input((self.generator_in_channels,))
352
+ x = layers.Dense(8 * 8 * self._seq_len)(g_input)
353
+ x = layers.LeakyReLU(alpha=0.2)(x)
354
+ x = layers.Reshape((self._seq_len, 64))(x)
355
+ x = layers.Conv1DTranspose(128, 4, strides=2, padding="same")(x)
356
+ x = layers.LeakyReLU(alpha=0.2)(x)
357
+ x = layers.Conv1DTranspose(128, 4, strides=2, padding="same")(x)
358
+ x = layers.LeakyReLU(alpha=0.2)(x)
359
+ x = layers.Conv1DTranspose(128, 4, strides=2, padding="same")(x)
360
+ x = layers.LeakyReLU(alpha=0.2)(x)
361
+ x = layers.Conv1DTranspose(128, 4, strides=2, padding="same")(x)
362
+ x = layers.LeakyReLU(alpha=0.2)(x)
363
+ x = layers.Conv1D(1, 8, padding="same")(x)
364
+ x = layers.LSTM(256, return_sequences=True)(x)
365
+
366
+ pool_and_stride = round((x.shape[1] + 1) / (self._seq_len + 1))
367
+
368
+ x = layers.AveragePooling1D(pool_size=pool_and_stride, strides=pool_and_stride)(x)
369
+ g_output = layers.LocallyConnected1D(self._feat_dim, 1, activation="tanh")(x)
370
+ generator = keras.Model(g_input, g_output, name="generator")
371
+ return generator
372
+
373
+
374
+ # class BaseClassificationArchitecture(Architecture):
375
+ # arch_type = "downstream:classification"
376
+ #
377
+ # def __init__(self, seq_len: int, feat_dim: int, output_dim: int) -> None:
378
+ # self._seq_len = seq_len
379
+ # self._feat_dim = feat_dim
380
+ # self._output_dim = output_dim
381
+ # self._model = self._build_model()
382
+ #
383
+ # @property
384
+ # def model(self) -> keras.models.Model:
385
+ # return self._model
386
+ #
387
+ # def get(self) -> T.Dict:
388
+ # return {"model": self.model}
389
+ #
390
+ # def _build_model(self) -> None:
391
+ # raise NotImplementedError
392
+
393
+
394
+ # class ConvnArchitecture(BaseClassificationArchitecture):
395
+ # def __init__(
396
+ # self, seq_len: int, feat_dim: int, output_dim: int, n_conv_blocks: int = 1
397
+ # ) -> None:
398
+ # self._n_conv_blocks = n_conv_blocks
399
+ # super().__init__(seq_len, feat_dim, output_dim)
400
+ #
401
+ # def _build_model(self) -> keras.models.Model:
402
+ # m_input = keras.Input((self._seq_len, self._feat_dim))
403
+ # x = m_input
404
+ # for _ in range(self._n_conv_blocks):
405
+ # x = layers.Conv1D(filters=64, kernel_size=3, activation="relu")(x)
406
+ # x = layers.Dropout(0.2)(x)
407
+ # x = layers.Flatten()(x)
408
+ # x = layers.Dense(128, activation="relu")(x)
409
+ # m_output = layers.Dense(self._output_dim, activation="softmax")(x)
410
+ # return keras.Model(m_input, m_output, name="classification_model")
411
+
412
+ # class ConvnLSTMnArchitecture(BaseClassificationArchitecture):
413
+ # def __init__(
414
+ # self, seq_len: int, feat_dim: int, output_dim: int, n_conv_lstm_blocks: int = 1
415
+ # ) -> None:
416
+ # self._n_conv_lstm_blocks = n_conv_lstm_blocks
417
+ # super().__init__(seq_len, feat_dim, output_dim)
418
+ #
419
+ # def _build_model(self) -> keras.models.Model:
420
+ # m_input = keras.Input((self._seq_len, self._feat_dim))
421
+ # x = m_input
422
+ # for _ in range(self._n_conv_lstm_blocks):
423
+ # x = layers.Conv1D(filters=64, kernel_size=3, activation="relu")(x)
424
+ # x = layers.Dropout(0.2)(x)
425
+ # x = layers.LSTM(128, activation="relu", return_sequences=True)(x)
426
+ # x = layers.Dropout(0.2)(x)
427
+ # x = layers.Flatten()(x)
428
+ # x = layers.Dense(128, activation="relu")(x)
429
+ # m_output = layers.Dense(self._output_dim, activation="softmax")(x)
430
+ # return keras.Model(m_input, m_output, name="classification_model")
431
+
432
+ class BasicRecurrentArchitecture(Architecture):
433
+ arch_type = "rnn_architecture"
434
+
435
+ def __init__(
436
+ self,
437
+ hidden_dim: int,
438
+ output_dim: int,
439
+ n_layers: int,
440
+ network_type: str,
441
+ name: str = "Sequential",
442
+ ) -> None:
443
+ """
444
+ :param hidden_dim: int, the number of units (e.g. 24)
445
+ :param output_dim: int, the number of output units (e.g. 1)
446
+ :param n_layers: int, the number of layers (e.g. 3)
447
+ :param network_type: str, one of 'gru', 'lstm', or 'lstmLN'
448
+ :param name: str, model name
449
+ Default: "Sequential"
450
+ """
451
+ self.hidden_dim = hidden_dim
452
+ self.output_dim = output_dim
453
+ self.n_layers = n_layers
454
+
455
+ self.network_type = network_type.lower()
456
+ assert self.network_type in ["gru", "lstm"]
457
+
458
+ self._name = name
459
+
460
+ def _rnn_cell(self) -> keras.layers.Layer:
461
+ """
462
+ Basic RNN Cell
463
+ :return cell: keras.layers.Layer
464
+ """
465
+ cell = None
466
+ # GRU
467
+ if self.network_type == "gru":
468
+ cell = keras.layers.GRUCell(self.hidden_dim, activation="tanh")
469
+ # LSTM
470
+ elif self.network_type == "lstm":
471
+ cell = keras.layers.LSTMCell(self.hidden_dim, activation="tanh")
472
+ return cell
473
+
474
+ def _make_network(self, model: keras.models.Model, activation: str, return_sequences: bool) -> keras.models.Model:
475
+ _cells = tf.keras.layers.StackedRNNCells(
476
+ [self._rnn_cell() for _ in range(self.n_layers)],
477
+ name=f"{self.network_type}_x{self.n_layers}",
478
+ )
479
+ model.add(keras.layers.RNN(_cells, return_sequences=return_sequences))
480
+ model.add(
481
+ keras.layers.Dense(units=self.output_dim, activation=activation, name="OUT")
482
+ )
483
+ return model
484
+
485
+ def build(self, activation: str = "sigmoid", return_sequences: bool = True) -> keras.models.Model:
486
+ model = keras.models.Sequential(name=f"{self._name}")
487
+ model = self._make_network(model, activation=activation, return_sequences=return_sequences)
488
+ return model
489
+
490
+
491
+ class Zoo(dict):
492
+ def __init__(self, *arg, **kwargs) -> None:
493
+ super(Zoo, self).__init__(*arg, **kwargs)
494
+
495
+ def summary(self) -> None:
496
+ summary_table = PrettyTable()
497
+ summary_table.field_names = ["id", "type"]
498
+ for k, v in self.items():
499
+ summary_table.add_row([k, v.arch_type])
500
+ print(summary_table)
501
+
502
+
503
+ zoo = Zoo(
504
+ {
505
+ # Generative models
506
+ "vae_conv5": VAE_CONV5Architecture,
507
+ "cvae_conv5": cVAE_CONV5Architecture,
508
+ "gan_conv_lstm": GAN_ConvLSTM4Architecture,
509
+ "gan_conv_lstm_2": GAN_Conv2LSTM4Architecture,
510
+ "gan_conv_lstm_3": GAN_Conv3LSTM4Architecture
511
+
512
+ # # Downstream models
513
+ # "clf_cn": ConvnArchitecture,
514
+ # "clf_cl_n": ConvnLSTMnArchitecture,
515
+ # "recurrent": BasicRecurrentArchitecture,
516
+ }
517
+ )
app.py ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import numpy as np
3
+ import matplotlib.pyplot as plt
4
+ from GAN.diffusion import build_model, GaussianDiffusion, DiffusionModel
5
+ import tensorflow as tf
6
+ from tensorflow.python.types.core import TensorLike
7
+ import imageio
8
+ import tempfile
9
+ import os
10
+
11
+ EPS = 1e-18
12
+ class TSFeatureScaler:
13
+ """Global time series scaler that scales all features to [0,1] then normalizes to [-1,1]"""
14
+
15
+ def __init__(self) -> None:
16
+ self.min_val = None
17
+ self.max_val = None
18
+
19
+ def fit(self, X: TensorLike) -> "TSFeatureScaler":
20
+ """
21
+ Fit scaler to data
22
+
23
+ Args:
24
+ X: Input tensor of shape [N, T, D]
25
+ (N: samples, T: timesteps, D: features)
26
+ """
27
+ # 计算整个数据集的全局最大最小值
28
+ self.min_val = np.min(X)
29
+ self.max_val = np.max(X)
30
+ return self
31
+
32
+ def transform(self, X: TensorLike) -> TensorLike:
33
+ """
34
+ Transform data in two steps:
35
+ 1. Scale to [0,1] using min-max scaling
36
+ 2. Normalize to [-1,1]
37
+ """
38
+ if self.min_val is None or self.max_val is None:
39
+ raise ValueError("Scaler must be fitted before transform")
40
+
41
+ # 1. 缩放到[0,1]
42
+ X_scaled = (X - self.min_val) / (self.max_val - self.min_val + EPS)
43
+
44
+ # 2. 归一化到[-1,1]
45
+ X_normalized = 2.0 * X_scaled - 1.0
46
+
47
+ return X_normalized
48
+
49
+ def fit_transform(self, X: TensorLike) -> TensorLike:
50
+ """Fit to data, then transform it"""
51
+ return self.fit(X).transform(X)
52
+
53
+
54
+ def create_animation(frames, fps=1):
55
+ """将帧列表转换为GIF动画数据"""
56
+ import tempfile
57
+ import os
58
+
59
+ temp_dir = tempfile.gettempdir()
60
+ temp_path = os.path.join(temp_dir, f"temp_{id(frames)}.gif")
61
+
62
+ # 将fps转换为duration (毫秒)
63
+ duration = int(1000 / fps) # 1000ms = 1s
64
+
65
+ # 保存为GIF文件,设置循环播放
66
+ imageio.mimsave(temp_path, frames, format='GIF', duration=duration, loop=0) # loop=0 表示无限循环
67
+
68
+ return temp_path
69
+
70
+ def generate_timeseries(input_file, num_samples=16):
71
+ try:
72
+ # 加载数据
73
+ real_data = np.load(input_file.name)
74
+ scaler = TSFeatureScaler()
75
+ real_data = scaler.fit_transform(real_data)
76
+ print(f"Loaded data shape: {real_data.shape}")
77
+
78
+ # 确保数据形状正确
79
+ expected_shape = (None, 96, 3)
80
+ if len(real_data.shape) != 3 or real_data.shape[1:] != expected_shape[1:]:
81
+ return None, None
82
+
83
+ # 创建模型和必要的组件
84
+ network = build_model(
85
+ time_len=96,
86
+ fea_num=3,
87
+ d_model=16,
88
+ n_heads=2,
89
+ encoder_type='dual'
90
+ )
91
+ ema_network = build_model(
92
+ time_len=96,
93
+ fea_num=3,
94
+ d_model=16,
95
+ n_heads=2,
96
+ encoder_type='dual'
97
+ )
98
+ ema_network.set_weights(network.get_weights())
99
+ noise_util = GaussianDiffusion(timesteps=10)
100
+
101
+ print("Creating model...")
102
+ model = DiffusionModel(
103
+ network=network,
104
+ ema_network=ema_network,
105
+ timesteps=10,
106
+ gdf_util=noise_util,
107
+ data=real_data[:num_samples]
108
+ )
109
+
110
+ # 加载预训练权重
111
+ checkpoint_path = "/Users/lindan/Dropbox/PhD/Projects/PLF/GAN/code_github/checkpoint/cp.ckpt"
112
+ print(f"Loading weights from {checkpoint_path}")
113
+ model.load_weights(checkpoint_path)
114
+
115
+
116
+ # 生成加噪过程的动画
117
+ print("Generating noising animation...")
118
+ noise_frames = model.plot_noise_process_app(num_samples)
119
+ noise_gif = create_animation(noise_frames)
120
+
121
+ # 生成去噪过程的动画
122
+ print("Generating denoising animation...")
123
+ denoise_frames = model.plot_denoise_process_app(num_samples)[1:]
124
+ denoise_gif = create_animation(denoise_frames)
125
+
126
+ return noise_gif, denoise_gif
127
+
128
+ except Exception as e:
129
+ import traceback
130
+ error_msg = f"Error: {str(e)}\n{traceback.format_exc()}"
131
+ print(error_msg)
132
+ return None, None
133
+
134
+ def update_example_gifs(num_samples):
135
+ """根据选择的样本数更新示例GIF"""
136
+ return f"noising_example_{num_samples}.gif", f"denoising_example_{num_samples}.gif"
137
+
138
+ # 创建Gradio界面
139
+ with gr.Blocks(title="Wearable Sensors Time-Series Generation") as demo:
140
+ with gr.Column(elem_id="container"):
141
+ # Logo
142
+ gr.Image("logo.webp", elem_id="logo", show_label=False, container=False)
143
+
144
+ # 标题和副标题
145
+ gr.Markdown(
146
+ """
147
+ # Wearable Sensors Time-Series Generation
148
+
149
+ <h3 style='font-weight: normal; color: #666;'>-- mainly targeted at livestock wearables sensors data</h3>
150
+ """)
151
+
152
+ with gr.Row():
153
+ with gr.Column():
154
+ noise_gif = gr.Image(value="noising_example_16.gif", label="Noising Process", show_label=True)
155
+ with gr.Column():
156
+ denoise_gif = gr.Image(value="denoising_example_16.gif", label="Denoising Process", show_label=True)
157
+
158
+ with gr.Row():
159
+ with gr.Column():
160
+
161
+ num_samples = gr.Radio(
162
+ choices=[4, 9, 16, 25],
163
+ value=16,
164
+ label="Number of samples to generate"
165
+ )
166
+ generate_btn = gr.Button("Generate")
167
+
168
+ # 将File组件改为Examples组件
169
+ input_file = gr.File(label="Select example data")
170
+ gr.Examples(
171
+ examples=[
172
+ ["app_examples/example1.npy"],
173
+ ["app_examples/example2.npy"],
174
+ ["app_examples/example3.npy"],
175
+ ["app_examples/example4.npy"]
176
+ ],
177
+ inputs=input_file,
178
+ label="Example Datasets"
179
+ )
180
+
181
+
182
+
183
+ # 添加按钮事件处理
184
+ generate_btn.click(
185
+ fn=generate_timeseries,
186
+ inputs=[input_file, num_samples],
187
+ outputs=[noise_gif, denoise_gif]
188
+ )
189
+
190
+ # 添加样本数选择的事件处理
191
+ num_samples.change(
192
+ fn=update_example_gifs,
193
+ inputs=[num_samples],
194
+ outputs=[noise_gif, denoise_gif]
195
+ )
196
+
197
+ # 添加CSS样式
198
+ gr.HTML(
199
+ """
200
+ <style>
201
+ #container {
202
+ text-align: center;
203
+ padding: 2rem 0;
204
+ }
205
+ #logo {
206
+ width: 120px;
207
+ height: 120px;
208
+ margin: 0 auto;
209
+ margin-bottom: 1rem;
210
+ }
211
+ h1 {
212
+ font-size: 3.5rem;
213
+ margin-bottom: 0.5rem;
214
+ }
215
+ h3 {
216
+ font-size: 1.8rem;
217
+ margin-top: 0;
218
+ color: #666;
219
+ }
220
+ </style>
221
+ """
222
+ )
223
+
224
+ # 启动应用
225
+ if __name__ == "__main__":
226
+ demo.launch(share=True)
app_examples/.DS_Store ADDED
Binary file (6.15 kB). View file
 
app_examples/example1.npy ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b82cbcf8248355a0c78e9bd5cf48d580363fb70e7b36fdf0bc1ec4ab96c77fe2
3
+ size 20864
app_examples/example2.npy ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7c81e71805ffaa6b0f7c74a6046ae2d55c86136ac668fe2517a4e1646f450773
3
+ size 18560
app_examples/example3.npy ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:620459ceb06b59e8b4c9e24b3b4b99a06cab624a632e9f1f4de1166f0f49a1b6
3
+ size 41600
app_examples/example4.npy ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1291113ea1aaea7c8bc9e138abcc9bc1213741960ca23a77f6f41ab069318499
3
+ size 20864
checkpoint/checkpoint ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ model_checkpoint_path: "cp.ckpt"
2
+ all_model_checkpoint_paths: "cp.ckpt"
checkpoint/cp.ckpt.data-00000-of-00001 ADDED
Binary file (989 kB). View file
 
checkpoint/cp.ckpt.index ADDED
Binary file (19.2 kB). View file
 
denoising_example_16.gif ADDED

Git LFS Details

  • SHA256: 1a3aadeb7a919d750055c349d1bbabe87e5a5c57ddfe44204a8fb4b2b4f6c526
  • Pointer size: 133 Bytes
  • Size of remote file: 10.5 MB
denoising_example_25.gif ADDED

Git LFS Details

  • SHA256: 837aea9f5969a4398f817bd60a7f1527ea15a65977a9cb91de59ba7035bb7374
  • Pointer size: 133 Bytes
  • Size of remote file: 15.6 MB
denoising_example_4.gif ADDED

Git LFS Details

  • SHA256: 79ad0170fa4de1564929b50305a2c2db0a368d572f013d56b89e80accd20a9bd
  • Pointer size: 132 Bytes
  • Size of remote file: 2.43 MB
denoising_example_9.gif ADDED

Git LFS Details

  • SHA256: 550513d7b98ee88af68a89c568f9d4df40fc79434cc0ddd7f44bed279ab28d11
  • Pointer size: 132 Bytes
  • Size of remote file: 5.67 MB
logo.webp ADDED
noising_example_16.gif ADDED

Git LFS Details

  • SHA256: ffc2583bce2cca955460681a9cdf598921b128af5406ffb7237620b0f2743923
  • Pointer size: 133 Bytes
  • Size of remote file: 10.8 MB
noising_example_25.gif ADDED

Git LFS Details

  • SHA256: e3dd422a7f73ad588a18a9e07f72f4c087cae79f819b48e62e9388aacd4898dd
  • Pointer size: 133 Bytes
  • Size of remote file: 15.1 MB
noising_example_4.gif ADDED

Git LFS Details

  • SHA256: d3df58a031e63ca5ee7e6eb0c8262334825b5c3e412e10a5212efa063e0768fa
  • Pointer size: 132 Bytes
  • Size of remote file: 2.54 MB
noising_example_9.gif ADDED

Git LFS Details

  • SHA256: bcbafd959f9c6be87d510ea199766cfd5b99038684b572b4b3ee3c39ed3e3d9b
  • Pointer size: 132 Bytes
  • Size of remote file: 6.17 MB