Contents

20250619 meeting

Introduction

To integrate $MRTS$ into the $SSSD^{S4}$ model and perform prediction, the related code for storing predictions was modified.

Error Message

The following error occurred when executing the original code.

Execution result reference
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
(sssd) u6025091@omciystest1140615-2jhh4:~/SSSD_CP$ ./scripts/diffusion/inference_job.sh -m configs/model.yaml -i configs/inference.yaml
Intializing conda
Activating Conda Env: sssd
[Execution - Inference]
/home/u6025091/SSSD_CP/scripts/diffusion/infer.py --model_config configs/model.yaml --inference_config configs/inference.yaml
2025-06-15 12:01:12,468 - sssd.utils.logger - INFO - Using 1 GPUs!
2025-06-15 12:01:12,699 - sssd.utils.logger - INFO - Current time: 2025-06-15 12:01:12
2025-06-15 12:01:40,463 - sssd.utils.logger - INFO - The 1th inference trial
2025-06-15 12:01:40,464 - sssd.utils.logger - INFO - Output directory: ./results/real_time/train_200_with_custom/tf/200/original/tf/T200_beta00.0001_betaT0.02/max
2025-06-15 12:01:45,795 - sssd.utils.logger - INFO - Successfully loaded model at iteration 2700
Traceback (most recent call last):
  File "/home/u6025091/SSSD_CP/scripts/diffusion/infer.py", line 117, in <module>
    run_job(model_config, inference_config, device, args.ckpt_iter)
  File "/home/u6025091/SSSD_CP/scripts/diffusion/infer.py", line 96, in run_job
    ).generate()
  File "/home/u6025091/.local/lib/python3.10/site-packages/sssd/inference/generator.py", line 144, in generate
    sampling(
  File "/home/u6025091/.local/lib/python3.10/site-packages/sssd/utils/utils.py", line 155, in sampling
    epsilon_theta = net(
  File "/home/u6025091/.local/lib/python3.10/site-packages/torch/nn/modules/module.py", line 1511, in _wrapped_call_impl
    return self._call_impl(*args, **kwargs)
  File "/home/u6025091/.local/lib/python3.10/site-packages/torch/nn/modules/module.py", line 1520, in _call_impl
    return forward_call(*args, **kwargs)
  File "/home/u6025091/.local/lib/python3.10/site-packages/torch/nn/parallel/data_parallel.py", line 183, in forward
    return self.module(*inputs[0], **module_kwargs[0])
  File "/home/u6025091/.local/lib/python3.10/site-packages/torch/nn/modules/module.py", line 1511, in _wrapped_call_impl
    return self._call_impl(*args, **kwargs)
  File "/home/u6025091/.local/lib/python3.10/site-packages/torch/nn/modules/module.py", line 1520, in _call_impl
    return forward_call(*args, **kwargs)
  File "/home/u6025091/.local/lib/python3.10/site-packages/sssd/core/imputers/SSSDS4Imputer.py", line 242, in forward
    noise, conditional, mask, diffusion_steps, frk_basis = input_data
ValueError: not enough values to unpack (expected 5, got 4)

From the error message, it can be seen that the error occurred in the function located in /sssd/core/imputers/SSSDS4Imputer.py.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    def forward(self, input_data):
        noise, conditional, mask, diffusion_steps, frk_basis = input_data

        # Handle mask and concatenate it to the conditional input
        conditional = conditional * mask
        conditional = torch.cat([conditional, mask.float(), frk_basis], dim=1)

        # Forward pass through the network
        x = noise  # Ensure x is 3D (B, C, L)
        x = self.init_conv(x)
        x = self.residual_layer((x, conditional, diffusion_steps))
        y = self.final_conv(x)

        return y

In line 2 of the function, it is indicated that input_data contains only 4 elements, but the code attempts to unpack it into 5 variables. Tracing the source reveals that the issue originates from a function in /sssd/inference/generator.py.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
def sampling(
    net: torch.nn.Module,
    size: Tuple[int, int, int],
    diffusion_hyperparams: Dict[str, torch.Tensor],
    cond: torch.Tensor,
    mask: torch.Tensor,
    only_generate_missing: int = 0,
    device: Union[torch.device, str] = "cpu",
) -> torch.Tensor:
    """
    Perform the complete sampling step according to p(x_0|x_T) = \prod_{t=1}^T p_{\theta}(x_{t-1}|x_t).

    Args:
        net (torch.nn.Module): The wavenet model.
        size (Tuple[int, int, int]): Size of tensor to be generated, usually (number of audios to generate, channels=1, length of audio).
        diffusion_hyperparams (Dict[str, torch.Tensor]): Dictionary of diffusion hyperparameters returned by calc_diffusion_hyperparams. Note, the tensors need to be cuda tensors.
        cond (torch.Tensor): Conditioning tensor.
        mask (torch.Tensor): Mask tensor.
        only_generate_missing (int, optional): Flag indicating whether to only generate missing values (default is 0).
        device (Union[torch.device, str], optional): Device to place tensors (default is 'cpu').

    Returns:
        torch.Tensor: The generated audio(s) in torch.Tensor, shape=size.
    """

    _dh = diffusion_hyperparams
    T, Alpha, Alpha_bar, Sigma = _dh["T"], _dh["Alpha"], _dh["Alpha_bar"], _dh["Sigma"]
    assert len(Alpha) == T
    assert len(Alpha_bar) == T
    assert len(Sigma) == T
    assert len(size) == 3

    x = std_normal(size, device)

    with torch.no_grad():
        for t in range(T - 1, -1, -1):
            if only_generate_missing == 1:
                x = x * (1 - mask).float() + cond * mask.float()
            diffusion_steps = (t * torch.ones((size[0], 1))).to(
                device
            )  # use the corresponding reverse step
            epsilon_theta = net(
                (x, cond, mask, diffusion_steps)
            )  # predict \epsilon according to \epsilon_\theta
            # update x_{t-1} to \mu_\theta(x_t)
            x = (
                x - (1 - Alpha[t]) / torch.sqrt(1 - Alpha_bar[t]) * epsilon_theta
            ) / torch.sqrt(Alpha[t])
            if t > 0:
                x = x + Sigma[t] * std_normal(
                    size, device
                )  # add the variance term to x_{t-1}

    return x

The error occurs in line 42, where the parameter frk_basis should be included. Therefore, the modification is as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
def sampling(
    net: torch.nn.Module,
    size: Tuple[int, int, int],
    diffusion_hyperparams: Dict[str, torch.Tensor],
    cond: torch.Tensor,
    mask: torch.Tensor,
    frk_basis: torch.Tensor,
    only_generate_missing: int = 0,
    device: Union[torch.device, str] = "cpu",
) -> torch.Tensor:
    """
    Perform the complete sampling step according to p(x_0|x_T) = \prod_{t=1}^T p_{\theta}(x_{t-1}|x_t).

    Args:
        net (torch.nn.Module): The wavenet model.
        size (Tuple[int, int, int]): Size of tensor to be generated, usually (number of audios to generate, channels=1, length of audio).
        diffusion_hyperparams (Dict[str, torch.Tensor]): Dictionary of diffusion hyperparameters returned by calc_diffusion_hyperparams. Note, the tensors need to be cuda tensors.
        cond (torch.Tensor): Conditioning tensor.
        mask (torch.Tensor): Mask tensor.
        only_generate_missing (int, optional): Flag indicating whether to only generate missing values (default is 0).
        device (Union[torch.device, str], optional): Device to place tensors (default is 'cpu').

    Returns:
        torch.Tensor: The generated audio(s) in torch.Tensor, shape=size.
    """

    _dh = diffusion_hyperparams
    T, Alpha, Alpha_bar, Sigma = _dh["T"], _dh["Alpha"], _dh["Alpha_bar"], _dh["Sigma"]
    assert len(Alpha) == T
    assert len(Alpha_bar) == T
    assert len(Sigma) == T
    assert len(size) == 3

    x = std_normal(size, device)

    with torch.no_grad():
        for t in range(T - 1, -1, -1):
            if only_generate_missing == 1:
                x = x * (1 - mask).float() + cond * mask.float()
            diffusion_steps = (t * torch.ones((size[0], 1))).to(
                device
            )  # use the corresponding reverse step
            epsilon_theta = net(
                (x, cond, mask, diffusion_steps, frk_basis)
            )  # predict \epsilon according to \epsilon_\theta
            # update x_{t-1} to \mu_\theta(x_t)
            x = (
                x - (1 - Alpha[t]) / torch.sqrt(1 - Alpha_bar[t]) * epsilon_theta
            ) / torch.sqrt(Alpha[t])
            if t > 0:
                x = x + Sigma[t] * std_normal(
                    size, device
                )  # add the variance term to x_{t-1}

    return x

Therefore, the function call from /sssd/inference/generator.py to this function also needs to be modified.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
    def generate(self) -> list:
        """Generate samples using the given neural network model."""
        all_mses = []
        all_mapes = []
        for index, (batch,) in enumerate(self.dataloader):
            batch = batch.to(self.device)
            mask = self._update_mask(batch)

            if torch.isnan(mask).any():  # debug
                print(f"[Batch {index}] NaN in mask!")

            batch = torch.nan_to_num(batch, nan=0.0)  # Replace NaN with 0.0

            if torch.isnan(batch).any():  # debug
                print(f"[NaN DETECTED] Batch {index} has NaNs!")

            batch = batch.permute(0, 2, 1)

            generated_series = (
                sampling(
                    net=self.net,
                    size=batch.shape,
                    diffusion_hyperparams=self.diffusion_hyperparams,
                    cond=batch,
                    mask=mask,
                    frk_basis=self.frk_basis,
                    only_generate_missing=self.only_generate_missing,
                    device=self.device,
                )
                .detach()
                .cpu()
                .numpy()
            )
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import logging
import os
from typing import Dict, Iterable, Optional, Union

import numpy as np
import torch
import torch.nn as nn
from sklearn.metrics import mean_absolute_percentage_error, mean_squared_error
from torch.utils.data import DataLoader

from sssd.core.model_specs import MASK_FN
from sssd.utils.logger import setup_logger
from sssd.utils.utils import find_max_epoch, sampling

from sssd.core.mrts.mrts import MRTS

LOGGER = setup_logger()


class DiffusionGenerator:
    """
    Generate data based on ground truth.

    Args:
        net (torch.nn.Module): The neural network model.
        device (Optional[Union[torch.device, str]]): The device to run the model on (e.g., 'cuda' or 'cpu').
        diffusion_hyperparams (dict): Dictionary of diffusion hyperparameters.
        local_path (str): Local path format for the model.
        testing_data (torch.Tensor): Tensor containing testing data.
        output_directory (str): Path to save generated samples.
        batch_size (int): Number of samples to generate.
        ckpt_path (str): Checkpoint directory.
        ckpt_iter (str): Pretrained checkpoint to load; 'max' selects the maximum iteration.
        masking (str): Type of masking: 'mnr' (missing not at random), 'bm' (black-out), 'rm' (random missing).
        missing_k (int): Number of missing time points for each channel across the length.
        only_generate_missing (int): Whether to generate only missing portions of the signal:
                                      - 0 (all sample diffusion),
                                      - 1 (generate missing portions only).
        saved_data_names (Iterable[str], optional): Names of data arrays to save (default is ("imputation", "original", "mask")).
        logger (Optional[logging.Logger], optional): Logger object for logging messages (default is None).
    """

    def __init__(
        self,
        net: torch.nn.Module,
        device: Optional[Union[torch.device, str]],
        diffusion_hyperparams: dict,
        local_path: str,
        dataloader: DataLoader,
        output_directory: str,
        batch_size: int,
        ckpt_path: str,
        ckpt_iter: str,
        masking: str,
        missing_k: int,
        only_generate_missing: int,
        saved_data_names: Iterable[str] = ("imputation", "original", "mask"),
        logger: Optional[logging.Logger] = None,
        locs: Optional[torch.Tensor] = None
    ):
        self.net = net
        self.device = device
        self.diffusion_hyperparams = diffusion_hyperparams
        self.local_path = local_path
        self.dataloader = dataloader
        self.batch_size = batch_size
        self.masking = masking
        self.missing_k = missing_k
        self.only_generate_missing = only_generate_missing
        self.logger = logger or LOGGER

        self.output_directory = self._prepare_output_directory(
            output_directory, local_path, ckpt_iter
        )
        self.saved_data_names = saved_data_names
        self._load_checkpoint(ckpt_path, ckpt_iter)


        self.locs = locs
        frk = MRTS(locs=self.locs, device=self.device)
        self.frk_basis = frk()
        batch_size_shape, time_shape, loc_shape = next(iter(dataloader))[0].shape
        
        #print(f"self.frk_basis is None, initializing...")
        frk_linear = nn.Linear(self.frk_basis.shape[1], time_shape).to(self.device)
        loc_embed = nn.Embedding(loc_shape, time_shape).to(self.device)

        self.frk_basis = frk_linear(self.frk_basis)  # [26, 29] → [26, 2000]
        loc_ids = torch.arange(loc_shape, device=self.device)
        self.frk_basis += loc_embed(loc_ids)  # 加入地點特徵
        self.frk_basis = self.frk_basis.unsqueeze(0).expand(batch_size_shape, -1, -1)  # [1, 26, 2000]
        self.frk_basis = self.frk_basis.detach()
        #print(f"frk unsqueeze shape: {self.frk_basis.shape}")
        #print(f"frk original shape: {self.frk_basis.shape}")

/scripts/diffusion/infer.py

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
import argparse
from typing import Optional, Union

import torch
import torch.nn as nn
import yaml

from sssd.core.model_specs import MODEL_PATH_FORMAT, setup_model
from sssd.data.utils import get_dataloader
from sssd.inference.generator import DiffusionGenerator
from sssd.utils.logger import setup_logger
from sssd.utils.utils import calc_diffusion_hyperparams, display_current_time

import numpy as np

LOGGER = setup_logger()


def fetch_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "-m",
        "--model_config",
        type=str,
        default="configs/model.yaml",
        help="Model configuration",
    )
    parser.add_argument(
        "-i",
        "--inference_config",
        type=str,
        default="configs/inference_config.yaml",
        help="Inference configuration",
    )
    parser.add_argument(
        "-ckpt_iter",
        "--ckpt_iter",
        default="max",
        help='Which checkpoint to use; assign a number or "max" to find the latest checkpoint',
    )
    return parser.parse_args()

# New function to load locations
def get_locations_loader(location_path, device: torch.device) -> torch.Tensor:
    locations = np.load(location_path)
    locations_tensor = torch.from_numpy(locations).float().to(device)
    return locations_tensor


def run_job(
    model_config: dict,
    inference_config: dict,
    device: Optional[Union[torch.device, str]],
    ckpt_iter: Union[str, int],
) -> None:
    trials = inference_config.get("trials")
    batch_size = inference_config["batch_size"]
    dataloader = get_dataloader(
        inference_config["data"]["test_path"],
        batch_size,
        device=device,
    )

    local_path = MODEL_PATH_FORMAT.format(
        T=model_config["diffusion"]["T"],
        beta_0=model_config["diffusion"]["beta_0"],
        beta_T=model_config["diffusion"]["beta_T"],
    )

    diffusion_hyperparams = calc_diffusion_hyperparams(
        **model_config["diffusion"], device=device
    )
    LOGGER.info(display_current_time())
    net = setup_model(inference_config["use_model"], model_config, device)

    # Check if multiple GPUs are available
    if torch.cuda.device_count() > 0:
        net = nn.DataParallel(net)

    data_names = ["imputation", "original", "mask"]
    directory = inference_config["output_directory"]

    if trials > 1:
        directory += "_{trial}"

    locations_loder = get_locations_loader(inference_config["data"]["location_path"], device)

    for trial in range(1, trials + 1):
        LOGGER.info(f"The {trial}th inference trial")
        saved_data_names = data_names if trial == 0 else data_names[0]

        mses, mapes = DiffusionGenerator(
            net=net,
            device=device,
            diffusion_hyperparams=diffusion_hyperparams,
            dataloader=dataloader,
            local_path=local_path,
            output_directory=directory.format(trial=trial) if trials > 1 else directory,
            ckpt_path=inference_config["ckpt_path"],
            ckpt_iter=ckpt_iter,
            batch_size=batch_size,
            masking=inference_config["masking"],
            missing_k=inference_config["missing_k"],
            only_generate_missing=inference_config["only_generate_missing"],
            saved_data_names=saved_data_names,
            locs=locations_loder,
        ).generate()

        LOGGER.info(f"Average MSE: {sum(mses) / len(mses)}")
        LOGGER.info(f"Average MAPE: {sum(mapes) / len(mapes)}")
        LOGGER.info(display_current_time())

/configs/inference.yaml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# Inference configuration
batch_size: 1  # Batch size for inference
output_directory: "./results/real_time/train_200_with_custom/tf/200/original/tf"  # Output directory for inference results
ckpt_path: "./results/real_time/checkpoints/test"  # Path to checkpoint for inference
trials: 1 # Replications

# Additional training settings
only_generate_missing: true  # Generate missing values only
use_model: 2  # Model to use for training
masking: "forecast"  # Masking strategy for missing values
missing_k: 200  # Number of missing values

# Data paths
data:
  test_path: "./datasets/real_time/pollutants_test.npy"  # Path to test data
  location_path: "./datasets/real_time/locations.npy"  # Path to location data

Execution

Training can still proceed normally. The MRTS function used is as follows:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
"""
Title: Multi-Resolution Thin-plate Spline basis function for Spatial Data, and calculate the basis function by using rectangular or spherical coordinates.
Author: Hsu, Yao-Chih
Version: 1140611
Reference: Resolution Adaptive Fixed Rank Kringing by ShengLi Tzeng & Hsin-Cheng Huang
"""

# import modules
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
from scipy.integrate import quad
from scipy.sparse.linalg import eigsh
from typing import Optional, Union
import logging
#import colorlog
#import datetime

## logger config
logger = logging.getLogger()
#handler = colorlog.StreamHandler()
#handler.setFormatter(colorlog.ColoredFormatter(
#    fmt='%(log_color)s%(asctime)s %(levelname)s: %(message)s',
#    datefmt='%Y-%m-%d %H:%M:%S',
#    log_colors={
#        'INFO': 'green',
#        'WARNING': 'yellow',
#        'ERROR': 'red',
#        'DEBUG': 'cyan',
#        'CRITICAL': 'bold_red',
#    }
#))
#logger.addHandler(handler)
#logger.setLevel(logging.INFO)

# classes
class MRTS(nn.Module):
    def __init__(self, locs: torch.Tensor, 
                 k: int=None, 
                 device: Optional[Union[torch.device, str]]=None, 
                 calculate_with_spherical: bool=False, 
                 standardize: bool=True, 
                 unbiased_std: bool=False
                 ):
        """
        Multi-Resolution Thin-plate Spline basis function.
        
        Args:
            locs: [N, d] tensor of spatial locations.
            k: (Optional) parameter for controlling resolution.
            device: (Optional) PyTorch device to use.
            calculate_with_spherical: (Optional) If True, use spherical coordinates for TPS calculation (default is False) (only supported when d == 2).
            standardize: (Optional) If True, perform standardization on locs (Recommended).
            unbiased_std: (Optional) If True, use unbiased standard deviation (dividing by N-1) (Recommended); 
                          if False, use biased standard deviation (dividing by N).
        """
        super().__init__()
        try:
            if device is None:
                self.device = torch.device('cpu')
                logger.warning(f'Parameter "device" was not set. Default value "cpu" is used.')
            else:
                self.device = torch.device(device)
                logger.info(f'Successfully using device "{self.device}".')
        except (TypeError, ValueError) as e:
            self.device = torch.device('cpu')
            logger.warning(f'Parameter "device" is not a valid device ({device}). Default value "cpu" is used. Error: {e}')

        self.register_buffer("locs", locs.to(self.device))
        self.N, self.d = locs.shape

        # number of basis
        max_k = self.N + self.d
        try:
            if k is None:
                self.k = max_k
                logger.warning(f'Parameter "k" was not set. Default value {self.k} is used.')
            elif 0 < k <= (max_k):
                self.k = k
            else:
                self.k = max_k
                logger.warning(f'Parameter "k" is out of valid range, it should be between 1 to {self.k}. Default value {self.k} is used.')
        except TypeError:
            self.k = max_k
            logger.warning(f'Parameter "k" is not an integer, the type you input is "{type(k).__name__}." Default value {self.k} is used.')

        self.calculate_with_spherical = calculate_with_spherical
        if type(self.calculate_with_spherical) is not bool:
            self.calculate_with_spherical = False
            logger.warning(f'Parameter "calculate_with_spherical" should be a boolean, the type you input is "{type(calculate_with_spherical).__name__}". Default value False is used.')
        elif self.calculate_with_spherical and self.d != 2:
            self.calculate_with_spherical = False
            logger.warning(f'Spherical TPS not implemented for d = {self.d}, only d = 2 is supported. Using rectangular coordinate system instead.')
        
        if self.calculate_with_spherical:
            logger.info(f'Calculate TPS with spherical coordinates.')
        else:
            logger.info(f'Calculate TPS with rectangular coordinates.')

        self.standardize = standardize
        if type(self.standardize) is not bool:
            self.standardize = True
            logger.warning(f'Parameter "standardize" should be a boolean, the type you input is "{type(standardize).__name__}". Default value True is used.')

        self.unbiased_std = unbiased_std
        if type(self.unbiased_std) is not bool:
            self.unbiased_std = False
            logger.warning(f'Parameter "unbiased_std" should be a boolean, the type you input is "{type(unbiased_std).__name__}". Default value False is used.')

    def _tps_phi(self, locs: torch.Tensor, calculate_with_spherical: bool=False) -> torch.Tensor:
        """
        Compute the Thin Plate Spline (TPS) radial basis matrix for input locations.

        Args:
            locs: [N, d] tensor of spatial locations.
            calculate_with_spherical: (Optional) If True, use spherical coordinates for TPS calculation (default is False) (only supported when d == 2).

        Returns:
            A [N, N] tensor representing the TPS radial basis matrix.
        """       

        # rectangular coordinate system
        if not calculate_with_spherical:
            return self.__calculate_phi_by_rectangular_coordinate(locs)
        
        # spherical coordinate system
        else:
            # convert to spherical coordinates
            ret = torch.zeros((self.N, self.N), device=locs.device)
            locs = locs * torch.pi / 180.0
            x = locs[:, 0]
            y = locs[:, 1]
            for i in range(self.N):
                for j in range(self.N):
                    ret[i, j] = self.__calculate_phi_by_spherical_coordinate(x[i], y[i], x[j], y[j])
            return ret

    def __calculate_phi_by_rectangular_coordinate(self, locs: torch.Tensor) -> torch.Tensor:
        """
        Calculate the TPS radial basis matrix using rectangular (Euclidean) coordinates.

        Args:
            locs: [N, d] tensor of spatial locations.

        Returns:
            A [N, N] tensor representing the TPS radial basis matrix.
        """
        dists = torch.cdist(locs, locs)  # Euclidean distances
        if self.d == 1:
            return (dists ** 3) / 12
        elif self.d == 2:
            mask = dists != 0
            ret = torch.zeros_like(dists)
            ret[mask] = (dists[mask] ** 2) * torch.log(dists[mask]) / (8 * torch.pi)
            return ret
        elif self.d == 3:
            return -dists / 8
        else:
            raise NotImplementedError(f"TPS not implemented for d = {self.d}")

    def __calculate_phi_by_spherical_coordinate(self, x1: float, y1: float, x2: float, y2: float) -> float:
        """
        Calculate the TPS radial basis function using spherical coordinates.
        
        Args:
            x1, y1: Spherical coordinates of the first point (in radians).
            x2, y2: Spherical coordinates of the second point (in radians). 

        Returns:
            A float representing the TPS radial basis function value.
        """
        cos_theta = torch.sin(x1) * torch.sin(x2) + torch.cos(x1) * torch.cos(x2) * torch.cos(y1 - y2)
        cos_theta = torch.clamp(cos_theta, -1.0, 1.0)
        #theta = torch.acos(cos_theta)
        ret = 1 - (torch.pi ** 2) / 6

        if not torch.isclose(cos_theta, torch.tensor(-1.0, device=cos_theta.device)):
            cos_theta = cos_theta.item()
            upper = (cos_theta + 1) / 2
            lower = 0
            result, _ = quad(self.__integrand_for_spherical_in_tps, lower, upper, epsabs=1e-6, epsrel=1e-6, limit=50)
            result = torch.tensor(result, dtype=torch.float32, device=self.device)
            ret -= result
        return ret
    
        # # method by GPT can just use tenser to calculate and efficient
        # # spherical coordinate system
        # locs_rad = locs * torch.pi / 180.0  # convert to radians
        # lat1 = locs_rad[:, 0].unsqueeze(1)  # [N, 1]
        # lon1 = locs_rad[:, 1].unsqueeze(1)  # [N, 1]
        # lat2 = locs_rad[:, 0].unsqueeze(0)  # [1, N]
        # lon2 = locs_rad[:, 1].unsqueeze(0)  # [1, N]

        # # Cosine of angular distance
        # cos_theta = (
        #     torch.sin(lat1) @ torch.sin(lat2) + torch.cos(lat1) @ torch.cos(lat2) * torch.cos(lon1 - lon2)
        # )
        # cos_theta = torch.clamp(cos_theta, -1.0, 1.0)

        # # Compute integral using trapezoidal rule
        # N = 100  # Number of steps in the integral approximation
        # x = torch.linspace(1e-6, 1.0, N, device=locs.device)  # avoid x=0
        # ln_term = torch.log(1 - x) / x  # [N]

        # def trapezoid_integrate(upper):  # upper: [N, N]
        #     upper = torch.clamp(upper, 1e-6, 1.0)
        #     area = torch.zeros_like(upper)
        #     for i in range(N - 1):
        #         x0, x1 = x[i], x[i + 1]
        #         y0, y1 = ln_term[i], ln_term[i + 1]
        #         h = x1 - x0
        #         area += h * (y0 + y1) / 2 * ((upper >= x1).float())  # only add where upper > x1
        #     return area

        # upper_bound = (cos_theta + 1) / 2  # [N, N]
        # integral_vals = trapezoid_integrate(upper_bound)  # [N, N]
        # ret = 1 - (torch.pi ** 2) / 6 - integral_vals
        # return ret
    
    def __integrand_for_spherical_in_tps(self, x):
        """
        Integrand function for the spherical TPS radial basis function.

        Args:
            x: The variable of integration.

        Returns:
            The value of the integrand at x.
        """
        return np.log(1 - x) / x
        
    def _standardize(self, x: torch.Tensor, unbiased_std: Optional[bool] = None) -> torch.Tensor:
        """
        A function to standardize the input tensor x.

        Args:
            x: Input tensor to be standardized.
            unbiased_std: (Optional) If True, use unbiased standard deviation (dividing by N-1); 
                          if False, use biased standard deviation (dividing by N).
                          Default is None, which uses the class attribute self.unbiased_std.

        Returns:
            A standardized tensor with mean 0 and standard deviation 1.
        """
        if unbiased_std is None:
            unbiased_std = self.unbiased_std
        mean = x.mean(dim=0, keepdim=True)  # keepdim=True to keep the same shape
        std = x.std(dim=0, unbiased=unbiased_std, keepdim=True)  # unbiased=False for population std
        std[std == 0] = 1.0
        x = (x - mean) / std
        return x

    def forward(self) -> torch.Tensor:
        """
        Forward pass to compute the basis functions. Constructs the basis functions based on the input locations and parameters.

        Returns:
            A tensor of shape [N, k] representing the basis functions.
        """

        # Construct X = [1, x1, x2, ..., xd]
        ones = torch.ones(self.N, 1, device=self.device)
        X = torch.cat([ones, self.locs], dim=1)  # [N, d+1]

        if self.k == 1:
            return ones
        elif self.k <= self.d:
            if self.standardize:
                X[:, 1:self.k] = self._standardize(X[:, 1:self.k])
            return X[:, :self.k]
        

        # TPS basis
        Phi = self._tps_phi(locs=self.locs, calculate_with_spherical=self.calculate_with_spherical)  # [N, N]
        
        # Q = I - X(XᵗX)⁻¹Xᵗ
        try:
            XtX_inv = torch.inverse(X.T @ X)  # [(d+1), (d+1)]
        except RuntimeError as e:
            logger.warning(f'torch.inverse failed due to {e}, this implies "X.T @ X" didn\'t have inverse. Using torch.linalg.pinv instead.')
            XtX_inv = torch.linalg.pinv(X.T @ X)
        Q = torch.eye(self.N, device=self.device) - X @ XtX_inv @ X.T

        # Eigen-decomposition
        G = Q @ Phi @ Q
        #eigenvalues, eigenvectors = torch.linalg.eigh(G)  # ascending order
        
        G = G.detach().cpu().numpy()
        eigenvalues, eigenvectors = eigsh(G, k=self.k - self.d - 1, which='LM')  # 'LM': Largest Magnitude, max k < N
        eigenvalues = torch.from_numpy(eigenvalues).to(self.device) 
        eigenvectors = torch.from_numpy(eigenvectors).to(self.device)

        # Filter out near-zero eigenvalues and sort descending
        #valid = eigenvalues > 1e-10
        #eigenvalues[~valid] *= -1.0  # make them positive
        #eigenvectors[:, ~valid] *= -1.0  # make them positive
        idx_desc = torch.argsort(eigenvalues, descending=True)
        eigenvalues = eigenvalues[idx_desc]
        eigenvectors = eigenvectors[:, idx_desc]

        Basis_high = eigenvectors * torch.sqrt(torch.tensor(self.N, dtype=torch.float32))

        # # Construct basis functions
        # Basis_high = (Phi.T - Phi @ X @ XtX_inv @ X.T).T @ eigenvectors @ torch.diag(1.0 / eigenvalues).to(self.device)
        
        # # the for loop
        # Basis_high_1 = torch.zeros(self.N, self.N, device=self.locs.device)
        # for i in range(self.N):
        #     phi_i = Phi[i, :]
        #     x_i = X[i, :]
        #     Phi_XXtXinv_x = (phi_i.T - Phi @ X @ XtX_inv @ x_i.T).T
        #     for j in range(self.N):
        #         lambda_j = 1 / eigenvalues[j]
        #         v_j = eigenvectors[:, j]
        #         Basis_high_1[i, j] = lambda_j * Phi_XXtXinv_x @ v_j
        # print(f'Basis_high:\n{Basis_high}, \n\nBasis_high_1:\n{Basis_high_1}')
        # print(torch.mean((Basis_high - Basis_high_1)**2))

        # Concatenate [1, x1, ..., xd] and B_high

        Basis_low = X  # [N, d+1]

        # Standardize
        if self.standardize:
            Basis_low[:, 1:(self.d + 1)] = self._standardize(Basis_low[:, 1:(self.d + 1)])

        F = torch.cat([Basis_low, Basis_high], dim=1)

        # Output k basis
        F = F[:, :self.k]

        # print('1')
        # eigenvalues = eigenvalues[:self.k - self.d - 1]  # Select top k - (d + 1) eigenvalues
        # eigenvectors = eigenvectors[:, :self.k - self.d - 1]  # Select top k - (d + 1) eigenvectors

        # # compute BBBH and UZ
        # BBB = XtX_inv @ X.T
        # BBBH = BBB @ Phi
        # BBB_gamma = BBB @ eigenvectors
        # B_BBB_gamma = X @ BBB_gamma
        # gammas = (eigenvectors - B_BBB_gamma) / eigenvalues.reshape(1, -1) * torch.sqrt(torch.tensor(self.N, dtype=torch.float32))
        # print('2')
        # col_sd = self.locs.std(dim=0, unbiased=True)
        # adjustment = torch.sqrt(torch.tensor(self.N / (self.N - 1), dtype=col_sd.dtype, device=col_sd.device))
        # diag_values = (adjustment / col_sd).cpu().numpy()
        # xobs_diag = np.diag(diag_values)
        # print('3')
        # # 初始化组合矩阵
        # UZ = np.zeros((self.N + self.d + 1, self.k))
        # print('4')
        # # 区块填充
        # UZ[:self.N, :self.k - self.d - 1] = gammas      # 左上区块
        # UZ[self.N, self.k - self.d - 1] = 1.0           # 中心单位元素
        # UZ[self.N + 1:, self.k - self.d:] = xobs_diag  # 右下对角区块

        return F  # [N, d+1 + k]
    
# functions


# main program
if __name__ == "__main__":
    # time
    start_time = datetime.datetime.now()

    # Load locations
    locations = np.load(f'locations.npy')

    print(f'locations:\n{locations}')
    print(f'locations shape: {locations.shape}')

    # Convert to tensor
    locs = torch.tensor(locations, dtype=torch.float32)

    # Create FRK basis
    device = 'cuda' if torch.cuda.is_available() else 'cpu'
    frk_basis = MRTS(locs, device=device, calculate_with_spherical=False)

    # Forward pass
    #F = pd.DataFrame(frk_basis().detach().cpu().numpy())
    F = pd.DataFrame(frk_basis())
    #.detach().cpu().numpy()

    print(f'F:\n{F}')
    # print(f'F sum:\n{np.sum(F**2, axis=0)}')
    # print(f'F shape: {F.shape}')

    # time
    end_time = datetime.datetime.now()
    use_time = end_time - start_time
    print(f'Use time: {use_time}')





    # bx = pd.DataFrame(np.load('bx.npy'))
    # print(f'bx:\n{bx}')
    # print(f'bx shape: {bx.shape}')
    # print(f'bx sum:\n{np.sum(bx**2, axis=0)}')


    # # Compare with bx
    # temp = F.iloc[:, :bx.shape[1]] - bx
    # print(f'F - bx:\n{temp}')
    # print(f'F - bx sum**2:\n{np.sum(temp**2, axis=0)}')

    # from scipy.optimize import linear_sum_assignment

    # # 假設 F 和 bx 是 numpy array
    # # 計算 cost matrix(F[i] 和 bx[j] 的差異平方)
    # cost_matrix = np.array([[np.sum((F[i] - bx[j])**2) for j in range(bx.shape[0])] for i in range(F.shape[0])])

    # # 使用匈牙利演算法找出最小成本配對
    # row_ind, col_ind = linear_sum_assignment(cost_matrix)

    # # 印出結果
    # for i, j in zip(row_ind, col_ind):
    #     print(f'F[{i}] 對應到 bx[{j}], 差異平方: {cost_matrix[i, j]:.4f}')

    # # 如果你想印出總差異平方和
    # total_cost = cost_matrix[row_ind, col_ind].sum()
    # print(f'總差異平方和: {total_cost:.4f}')

The execution process is as follows:

Execution result reference
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
(sssd) u6025091@uuidi1test1140615-9hs5j:~/SSSD_CP$ ./scripts/diffusion/training_job.sh -m configs/model.yaml -t configs/training.yaml
Intializing conda
Activating Conda Env: sssd
[Execution - Training]
/home/u6025091/SSSD_CP/scripts/diffusion/train.py --model_config configs/model.yaml --training_config configs/training.yaml
2025-06-15 16:02:21,232 - sssd.utils.logger - INFO - Model spec: {'wavenet': {'input_channels': 26, 'output_channels': 26, 'residual_layers': 36, 'residual_channels': 256, 'skip_channels': 256, 'diffusion_step_embed_dim_input': 128, 'diffusion_step_embed_dim_hidden': 512, 'diffusion_step_embed_dim_output': 512, 's4_max_sequence_length': 2000, 's4_state_dim': 64, 's4_dropout': 0.0, 's4_bidirectional': True, 's4_use_layer_norm': True}, 'diffusion': {'T': 200, 'beta_0': 0.0001, 'beta_T': 0.02}}
2025-06-15 16:02:21,232 - sssd.utils.logger - INFO - Training spec: {'batch_size': 1, 'output_directory': './results/real_time/checkpoints/test', 'ckpt_iter': 'max', 'iters_per_ckpt': 100, 'iters_per_logging': 100, 'n_iters': 2000, 'learning_rate': 0.001, 'only_generate_missing': True, 'use_model': 2, 'masking': 'forecast', 'missing_k': 200, 'data': {'train_path': './datasets/real_time/pollutants_train.npy', 'location_path': './datasets/real_time/locations.npy'}}
2025-06-15 16:02:21,244 - sssd.utils.logger - INFO - Using 1 GPUs!
2025-06-15 16:02:21,270 - sssd.utils.logger - INFO - Using device: cuda
2025-06-15 16:02:21,270 - sssd.utils.logger - INFO - Output directory ./results/real_time/checkpoints/test/T200_beta00.0001_betaT0.02
2025-06-15 16:02:56,388 - sssd.utils.logger - INFO - Current time: 2025-06-15 16:02:56
Parameter "k" was not set. Default value 28 is used.
self.frk_basis is None, initializing...
frk unsqueeze shape: torch.Size([1, 26, 2000])
2025-06-15 16:03:08,526 - sssd.utils.logger - INFO - Successfully loaded model at iteration 300
2025-06-15 16:03:08,530 - sssd.utils.logger - INFO - Start the 301 iteration
100%|██████████████████████████████████████████████████████████████████████████████████| 5/5 [00:09<00:00,  1.98s/it]
100%|██████████████████████████████████████████████████████████████████████████████████| 5/5 [00:06<00:00,  1.24s/it]
100%|██████████████████████████████████████████████████████████████████████████████████| 5/5 [00:06<00:00,  1.24s/it]
100%|██████████████████████████████████████████████████████████████████████████████████| 5/5 [00:06<00:00,  1.24s/it]
100%|██████████████████████████████████████████████████████████████████████████████████| 5/5 [00:06<00:00,  1.25s/it]

The GPU resources are as follows:

Execution result reference
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
Every 0.1s: nvidia-smi                   uuidi1test1140615-9hs5j: Sun Jun 15 16:06:30 2025

Sun Jun 15 16:06:30 2025
+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 535.161.08             Driver Version: 535.161.08   CUDA Version: 12.8     |
|-----------------------------------------+----------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |         Memory-Usage | GPU-Util  Compute M. |
|                                         |                      |               MIG M. |
|=========================================+======================+======================|
|   0  Tesla V100-SXM2-32GB           On  | 00000000:3D:00.0 Off |                    0 |
| N/A   31C    P0             197W / 300W |  27039MiB / 32768MiB |    100%      Default |
|                                         |                      |                  N/A |
+-----------------------------------------+----------------------+----------------------+

+---------------------------------------------------------------------------------------+
| Processes:                                                                            |
|  GPU   GI   CI        PID   Type   Process name                            GPU Memory |
|        ID   ID                                                             Usage      |
|=======================================================================================|
+---------------------------------------------------------------------------------------+

However, it should be noted that previously trained results cannot be used here, as the MRTS basis has changed. Therefore, retraining is required.

Findings

To save training time, the original project egpivo/SSSD_CP was reviewed, and it was found to contain incomplete descriptions, as shown in the figure below:

https://raw.githubusercontent.com/Josh-test-lab/website-assets-repository/refs/heads/main/posts/1140619%20meeting/螢幕擷取畫面%202025-06-15%20160957.png
The description from the original repository's README file.

This section does not clearly explain how to perform multi-GPU computation locally. Therefore, after studying PyTorch’s multi-GPU operation modes, the simpler approach below was chosen: using the torch.nn.DataParallel() function to split the model and train on multiple GPUs.

The following function in /scripts/diffusion/train.py was modified accordingly:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
def run_job(
    model_config: dict,
    training_config: dict,
    device: Optional[Union[torch.device, str]],
) -> None:
    output_directory = setup_output_directory(model_config, training_config)
    dataloader = get_dataloader(
        training_config["data"]["train_path"],
        batch_size=training_config.get("batch_size"),
        device=device,
    )

    locations_loder = get_locations_loader(training_config["data"]["location_path"], device)

    diffusion_hyperparams = calc_diffusion_hyperparams(
        **model_config["diffusion"], device=device
    )
    net = setup_model(training_config["use_model"], model_config, device)


    ## 包裝成 DataParallel(多 GPU)
    #if torch.cuda.device_count() > 1:
    #    LOGGER.info(f"Wrapping model with DataParallel across {torch.cuda.device_count()} GPUs")
    #    net = torch.nn.DataParallel(net)
    #net = net.to(device)


    LOGGER.info(display_current_time())
    trainer = DiffusionTrainer(
        dataloader=dataloader,
        diffusion_hyperparams=diffusion_hyperparams,
        net=net,
        device=device,
        output_directory=output_directory,
        ckpt_iter=training_config.get("ckpt_iter"),
        n_iters=training_config.get("n_iters"),
        iters_per_ckpt=training_config.get("iters_per_ckpt"),
        iters_per_logging=training_config.get("iters_per_logging"),
        learning_rate=training_config.get("learning_rate"),
        only_generate_missing=training_config.get("only_generate_missing"),
        masking=training_config.get("masking"),
        missing_k=training_config.get("missing_k"),
        batch_size=training_config.get("batch_size"),
        logger=LOGGER,
        locs=locations_loder,
    )
    trainer.train()

    LOGGER.info(display_current_time())

The commented sections indicate the newly added parts. Multi-GPU support should be enabled using CUDA_VISIBLE_DEVICES=0,1. An example is as follows:

Example
1
CUDA_VISIBLE_DEVICES=0,1 ./scripts/diffusion/training_job.sh -m configs/model.yaml -t configs/training.yaml

However, running on multiple GPUs (using 2 GPUs here) still results in errors for the following reasons:

  • The principle of DataParallel() is to evenly split the batch size across each GPU for computation, but since the training batch_size = 1, this is effectively the same as using a single GPU.
  • Even if the batch size is set to batch_size = 2, because the dataset shape is (5, 200, 26), the size 5 cannot be evenly divided across 2 GPUs, which may cause issues.
  • Some operations temporarily move data off the GPU, and when .to(device) is used again at the end, it can lead to a situation where some parts of the dataset are on GPU 0 and others on GPU 1, causing data fragmentation that prevents computation.

Therefore, due to the above errors, to use multi-GPU computation, the internal code must first unify the functions using .to(device) so that after external operations finish, computation can continue on the correct GPU. Since these modifications could be extensive, development has been paused for now to focus on data imputation first.

Inference

The result of inference:

Execution result reference
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
(sssd) u6025091@jn9kc9test1140617-xd5qh:~/SSSD_CP$ ./inference_per_miss.sh
執行 missing_k=12, masking=forecast ...
Intializing conda
Activating Conda Env: sssd
[Execution - Inference]
/home/u6025091/SSSD_CP/scripts/diffusion/infer.py --model_config configs/model.yaml --inference_config configs/inference.yaml
2025-06-17 14:24:49,760 - sssd.utils.logger - INFO - Using 1 GPUs!
2025-06-17 14:24:49,984 - sssd.utils.logger - INFO - Current time: 2025-06-17 14:24:49
2025-06-17 14:25:24,210 - sssd.utils.logger - INFO - The 1th inference trial
2025-06-17 14:25:24,212 - sssd.utils.logger - INFO - Output directory: ./results/real_time/train_12_with_custom/tf/12/original/forecast/T200_beta00.0001_betaT0.02/max
2025-06-17 14:25:29,735 - sssd.utils.logger - INFO - Successfully loaded model at iteration 2700
2025-06-17 14:25:29,755 - sssd.utils.logger - INFO - Successfully using device "cuda".
2025-06-17 14:25:29,755 - sssd.utils.logger - WARNING - Parameter "k" was not set. Default value 28 is used.
2025-06-17 14:25:29,755 - sssd.utils.logger - INFO - Calculate TPS with rectangular coordinates.
2025-06-17 14:32:49,991 - sssd.utils.logger - INFO - Average MSE: 0.04452039524912834
2025-06-17 14:32:49,991 - sssd.utils.logger - INFO - Average MAPE: 0.2639411389827728
2025-06-17 14:32:49,991 - sssd.utils.logger - INFO - Current time: 2025-06-17 14:32:49
Inference Job completed
執行 missing_k=12, masking=rm ...
Intializing conda
Activating Conda Env: sssd
[Execution - Inference]
/home/u6025091/SSSD_CP/scripts/diffusion/infer.py --model_config configs/model.yaml --inference_config configs/inference.yaml
2025-06-17 14:32:54,265 - sssd.utils.logger - INFO - Using 1 GPUs!
2025-06-17 14:32:54,417 - sssd.utils.logger - INFO - Current time: 2025-06-17 14:32:54
2025-06-17 14:33:27,286 - sssd.utils.logger - INFO - The 1th inference trial
2025-06-17 14:33:27,288 - sssd.utils.logger - INFO - Output directory: ./results/real_time/train_12_with_custom/tf/12/original/rm/T200_beta00.0001_betaT0.02/max
2025-06-17 14:33:28,326 - sssd.utils.logger - INFO - Successfully loaded model at iteration 2700
2025-06-17 14:33:28,349 - sssd.utils.logger - INFO - Successfully using device "cuda".
2025-06-17 14:33:28,349 - sssd.utils.logger - WARNING - Parameter "k" was not set. Default value 28 is used.
2025-06-17 14:33:28,349 - sssd.utils.logger - INFO - Calculate TPS with rectangular coordinates.
2025-06-17 14:40:46,003 - sssd.utils.logger - INFO - Average MSE: 0.03280486762523651
2025-06-17 14:40:46,003 - sssd.utils.logger - INFO - Average MAPE: 0.2803750991821289
2025-06-17 14:40:46,004 - sssd.utils.logger - INFO - Current time: 2025-06-17 14:40:46
Inference Job completed
執行 missing_k=12, masking=mnr ...
Intializing conda
Activating Conda Env: sssd
[Execution - Inference]
/home/u6025091/SSSD_CP/scripts/diffusion/infer.py --model_config configs/model.yaml --inference_config configs/inference.yaml
2025-06-17 14:40:50,223 - sssd.utils.logger - INFO - Using 1 GPUs!
2025-06-17 14:40:50,371 - sssd.utils.logger - INFO - Current time: 2025-06-17 14:40:50
2025-06-17 14:41:22,932 - sssd.utils.logger - INFO - The 1th inference trial
2025-06-17 14:41:22,934 - sssd.utils.logger - INFO - Output directory: ./results/real_time/train_12_with_custom/tf/12/original/mnr/T200_beta00.0001_betaT0.02/max
2025-06-17 14:41:24,044 - sssd.utils.logger - INFO - Successfully loaded model at iteration 2700
2025-06-17 14:41:24,060 - sssd.utils.logger - INFO - Successfully using device "cuda".
2025-06-17 14:41:24,060 - sssd.utils.logger - WARNING - Parameter "k" was not set. Default value 28 is used.
2025-06-17 14:41:24,061 - sssd.utils.logger - INFO - Calculate TPS with rectangular coordinates.
2025-06-17 14:48:41,825 - sssd.utils.logger - INFO - Average MSE: 0.054531391710042953
2025-06-17 14:48:41,826 - sssd.utils.logger - INFO - Average MAPE: 0.6076512455940246
2025-06-17 14:48:41,826 - sssd.utils.logger - INFO - Current time: 2025-06-17 14:48:41
Inference Job completed
執行 missing_k=12, masking=bm ...
Intializing conda
Activating Conda Env: sssd
[Execution - Inference]
/home/u6025091/SSSD_CP/scripts/diffusion/infer.py --model_config configs/model.yaml --inference_config configs/inference.yaml
2025-06-17 14:48:46,021 - sssd.utils.logger - INFO - Using 1 GPUs!
2025-06-17 14:48:46,138 - sssd.utils.logger - INFO - Current time: 2025-06-17 14:48:46
2025-06-17 14:49:18,674 - sssd.utils.logger - INFO - The 1th inference trial
2025-06-17 14:49:18,676 - sssd.utils.logger - INFO - Output directory: ./results/real_time/train_12_with_custom/tf/12/original/bm/T200_beta00.0001_betaT0.02/max
2025-06-17 14:49:19,781 - sssd.utils.logger - INFO - Successfully loaded model at iteration 2700
2025-06-17 14:49:19,832 - sssd.utils.logger - INFO - Successfully using device "cuda".
2025-06-17 14:49:19,832 - sssd.utils.logger - WARNING - Parameter "k" was not set. Default value 28 is used.
2025-06-17 14:49:19,832 - sssd.utils.logger - INFO - Calculate TPS with rectangular coordinates.
2025-06-17 14:56:37,841 - sssd.utils.logger - INFO - Average MSE: 0.08220569193363189
2025-06-17 14:56:37,842 - sssd.utils.logger - INFO - Average MAPE: 0.679029792547226
2025-06-17 14:56:37,842 - sssd.utils.logger - INFO - Current time: 2025-06-17 14:56:37
Inference Job completed
執行 missing_k=24, masking=forecast ...
Intializing conda
Activating Conda Env: sssd
[Execution - Inference]
/home/u6025091/SSSD_CP/scripts/diffusion/infer.py --model_config configs/model.yaml --inference_config configs/inference.yaml
2025-06-17 14:56:42,054 - sssd.utils.logger - INFO - Using 1 GPUs!
2025-06-17 14:56:42,167 - sssd.utils.logger - INFO - Current time: 2025-06-17 14:56:42
2025-06-17 14:57:15,155 - sssd.utils.logger - INFO - The 1th inference trial
2025-06-17 14:57:15,158 - sssd.utils.logger - INFO - Output directory: ./results/real_time/train_24_with_custom/tf/24/original/forecast/T200_beta00.0001_betaT0.02/max
2025-06-17 14:57:16,263 - sssd.utils.logger - INFO - Successfully loaded model at iteration 2700
2025-06-17 14:57:16,268 - sssd.utils.logger - INFO - Successfully using device "cuda".
2025-06-17 14:57:16,268 - sssd.utils.logger - WARNING - Parameter "k" was not set. Default value 28 is used.
2025-06-17 14:57:16,268 - sssd.utils.logger - INFO - Calculate TPS with rectangular coordinates.
2025-06-17 15:04:34,309 - sssd.utils.logger - INFO - Average MSE: 0.07742066234350205
2025-06-17 15:04:34,309 - sssd.utils.logger - INFO - Average MAPE: 0.2457350790500641
2025-06-17 15:04:34,310 - sssd.utils.logger - INFO - Current time: 2025-06-17 15:04:34
Inference Job completed
執行 missing_k=24, masking=rm ...
Intializing conda
Activating Conda Env: sssd
[Execution - Inference]
/home/u6025091/SSSD_CP/scripts/diffusion/infer.py --model_config configs/model.yaml --inference_config configs/inference.yaml
2025-06-17 15:04:38,616 - sssd.utils.logger - INFO - Using 1 GPUs!
2025-06-17 15:04:38,768 - sssd.utils.logger - INFO - Current time: 2025-06-17 15:04:38
2025-06-17 15:05:11,415 - sssd.utils.logger - INFO - The 1th inference trial
2025-06-17 15:05:11,417 - sssd.utils.logger - INFO - Output directory: ./results/real_time/train_24_with_custom/tf/24/original/rm/T200_beta00.0001_betaT0.02/max
2025-06-17 15:05:12,441 - sssd.utils.logger - INFO - Successfully loaded model at iteration 2700
2025-06-17 15:05:12,446 - sssd.utils.logger - INFO - Successfully using device "cuda".
2025-06-17 15:05:12,446 - sssd.utils.logger - WARNING - Parameter "k" was not set. Default value 28 is used.
2025-06-17 15:05:12,446 - sssd.utils.logger - INFO - Calculate TPS with rectangular coordinates.
2025-06-17 15:12:30,538 - sssd.utils.logger - INFO - Average MSE: 0.03197858259081841
2025-06-17 15:12:30,538 - sssd.utils.logger - INFO - Average MAPE: 0.30044960379600527
2025-06-17 15:12:30,538 - sssd.utils.logger - INFO - Current time: 2025-06-17 15:12:30
Inference Job completed
執行 missing_k=24, masking=mnr ...
Intializing conda
Activating Conda Env: sssd
[Execution - Inference]
/home/u6025091/SSSD_CP/scripts/diffusion/infer.py --model_config configs/model.yaml --inference_config configs/inference.yaml
2025-06-17 15:12:34,951 - sssd.utils.logger - INFO - Using 1 GPUs!
2025-06-17 15:12:35,111 - sssd.utils.logger - INFO - Current time: 2025-06-17 15:12:35
2025-06-17 15:13:08,193 - sssd.utils.logger - INFO - The 1th inference trial
2025-06-17 15:13:08,195 - sssd.utils.logger - INFO - Output directory: ./results/real_time/train_24_with_custom/tf/24/original/mnr/T200_beta00.0001_betaT0.02/max
2025-06-17 15:13:09,278 - sssd.utils.logger - INFO - Successfully loaded model at iteration 2700
2025-06-17 15:13:09,303 - sssd.utils.logger - INFO - Successfully using device "cuda".
2025-06-17 15:13:09,303 - sssd.utils.logger - WARNING - Parameter "k" was not set. Default value 28 is used.
2025-06-17 15:13:09,303 - sssd.utils.logger - INFO - Calculate TPS with rectangular coordinates.
2025-06-17 15:20:27,703 - sssd.utils.logger - INFO - Average MSE: 0.03101613149046898
2025-06-17 15:20:27,703 - sssd.utils.logger - INFO - Average MAPE: 0.4463586062192917
2025-06-17 15:20:27,703 - sssd.utils.logger - INFO - Current time: 2025-06-17 15:20:27
Inference Job completed
執行 missing_k=24, masking=bm ...
Intializing conda
Activating Conda Env: sssd
[Execution - Inference]
/home/u6025091/SSSD_CP/scripts/diffusion/infer.py --model_config configs/model.yaml --inference_config configs/inference.yaml
2025-06-17 15:20:31,906 - sssd.utils.logger - INFO - Using 1 GPUs!
2025-06-17 15:20:32,056 - sssd.utils.logger - INFO - Current time: 2025-06-17 15:20:32
2025-06-17 15:21:05,167 - sssd.utils.logger - INFO - The 1th inference trial
2025-06-17 15:21:05,168 - sssd.utils.logger - INFO - Output directory: ./results/real_time/train_24_with_custom/tf/24/original/bm/T200_beta00.0001_betaT0.02/max
2025-06-17 15:21:06,276 - sssd.utils.logger - INFO - Successfully loaded model at iteration 2700
2025-06-17 15:21:06,297 - sssd.utils.logger - INFO - Successfully using device "cuda".
2025-06-17 15:21:06,297 - sssd.utils.logger - WARNING - Parameter "k" was not set. Default value 28 is used.
2025-06-17 15:21:06,297 - sssd.utils.logger - INFO - Calculate TPS with rectangular coordinates.
2025-06-17 15:28:24,216 - sssd.utils.logger - INFO - Average MSE: 0.05375478081405163
2025-06-17 15:28:24,216 - sssd.utils.logger - INFO - Average MAPE: 0.5050143867731094
2025-06-17 15:28:24,216 - sssd.utils.logger - INFO - Current time: 2025-06-17 15:28:24
Inference Job completed
執行 missing_k=200, masking=forecast ...
Intializing conda
Activating Conda Env: sssd
[Execution - Inference]
/home/u6025091/SSSD_CP/scripts/diffusion/infer.py --model_config configs/model.yaml --inference_config configs/inference.yaml
2025-06-17 15:28:28,359 - sssd.utils.logger - INFO - Using 1 GPUs!
2025-06-17 15:28:28,498 - sssd.utils.logger - INFO - Current time: 2025-06-17 15:28:28
2025-06-17 15:29:01,936 - sssd.utils.logger - INFO - The 1th inference trial
2025-06-17 15:29:01,939 - sssd.utils.logger - INFO - Output directory: ./results/real_time/train_200_with_custom/tf/200/original/forecast/T200_beta00.0001_betaT0.02/max
2025-06-17 15:29:03,041 - sssd.utils.logger - INFO - Successfully loaded model at iteration 2700
2025-06-17 15:29:03,068 - sssd.utils.logger - INFO - Successfully using device "cuda".
2025-06-17 15:29:03,069 - sssd.utils.logger - WARNING - Parameter "k" was not set. Default value 28 is used.
2025-06-17 15:29:03,069 - sssd.utils.logger - INFO - Calculate TPS with rectangular coordinates.
2025-06-17 15:36:21,366 - sssd.utils.logger - INFO - Average MSE: 0.039383457601070405
2025-06-17 15:36:21,366 - sssd.utils.logger - INFO - Average MAPE: 0.2943285584449768
2025-06-17 15:36:21,367 - sssd.utils.logger - INFO - Current time: 2025-06-17 15:36:21
Inference Job completed
執行 missing_k=200, masking=rm ...
Intializing conda
Activating Conda Env: sssd
[Execution - Inference]
/home/u6025091/SSSD_CP/scripts/diffusion/infer.py --model_config configs/model.yaml --inference_config configs/inference.yaml
2025-06-17 15:36:25,440 - sssd.utils.logger - INFO - Using 1 GPUs!
2025-06-17 15:36:25,601 - sssd.utils.logger - INFO - Current time: 2025-06-17 15:36:25
2025-06-17 15:36:58,212 - sssd.utils.logger - INFO - The 1th inference trial
2025-06-17 15:36:58,213 - sssd.utils.logger - INFO - Output directory: ./results/real_time/train_200_with_custom/tf/200/original/rm/T200_beta00.0001_betaT0.02/max
2025-06-17 15:36:59,244 - sssd.utils.logger - INFO - Successfully loaded model at iteration 2700
2025-06-17 15:36:59,267 - sssd.utils.logger - INFO - Successfully using device "cuda".
2025-06-17 15:36:59,267 - sssd.utils.logger - WARNING - Parameter "k" was not set. Default value 28 is used.
2025-06-17 15:36:59,267 - sssd.utils.logger - INFO - Calculate TPS with rectangular coordinates.
2025-06-17 15:44:17,370 - sssd.utils.logger - INFO - Average MSE: 0.10192955136299134
2025-06-17 15:44:17,370 - sssd.utils.logger - INFO - Average MAPE: 0.5133300304412842
2025-06-17 15:44:17,370 - sssd.utils.logger - INFO - Current time: 2025-06-17 15:44:17
Inference Job completed
執行 missing_k=200, masking=mnr ...
Intializing conda
Activating Conda Env: sssd
[Execution - Inference]
/home/u6025091/SSSD_CP/scripts/diffusion/infer.py --model_config configs/model.yaml --inference_config configs/inference.yaml
2025-06-17 15:44:21,535 - sssd.utils.logger - INFO - Using 1 GPUs!
2025-06-17 15:44:21,704 - sssd.utils.logger - INFO - Current time: 2025-06-17 15:44:21
2025-06-17 15:44:55,144 - sssd.utils.logger - INFO - The 1th inference trial
2025-06-17 15:44:55,145 - sssd.utils.logger - INFO - Output directory: ./results/real_time/train_200_with_custom/tf/200/original/mnr/T200_beta00.0001_betaT0.02/max
2025-06-17 15:44:56,212 - sssd.utils.logger - INFO - Successfully loaded model at iteration 2700
2025-06-17 15:44:56,235 - sssd.utils.logger - INFO - Successfully using device "cuda".
2025-06-17 15:44:56,236 - sssd.utils.logger - WARNING - Parameter "k" was not set. Default value 28 is used.
2025-06-17 15:44:56,236 - sssd.utils.logger - INFO - Calculate TPS with rectangular coordinates.
2025-06-17 15:52:14,069 - sssd.utils.logger - INFO - Average MSE: 0.09088289812207222
2025-06-17 15:52:14,069 - sssd.utils.logger - INFO - Average MAPE: 0.7056139945983887
2025-06-17 15:52:14,069 - sssd.utils.logger - INFO - Current time: 2025-06-17 15:52:14
Inference Job completed
執行 missing_k=200, masking=bm ...
Intializing conda
Activating Conda Env: sssd
[Execution - Inference]
/home/u6025091/SSSD_CP/scripts/diffusion/infer.py --model_config configs/model.yaml --inference_config configs/inference.yaml
2025-06-17 15:52:18,488 - sssd.utils.logger - INFO - Using 1 GPUs!
2025-06-17 15:52:18,606 - sssd.utils.logger - INFO - Current time: 2025-06-17 15:52:18
2025-06-17 15:52:51,743 - sssd.utils.logger - INFO - The 1th inference trial
2025-06-17 15:52:51,745 - sssd.utils.logger - INFO - Output directory: ./results/real_time/train_200_with_custom/tf/200/original/bm/T200_beta00.0001_betaT0.02/max
2025-06-17 15:52:53,009 - sssd.utils.logger - INFO - Successfully loaded model at iteration 2700
2025-06-17 15:52:53,015 - sssd.utils.logger - INFO - Successfully using device "cuda".
2025-06-17 15:52:53,016 - sssd.utils.logger - WARNING - Parameter "k" was not set. Default value 28 is used.
2025-06-17 15:52:53,016 - sssd.utils.logger - INFO - Calculate TPS with rectangular coordinates.
2025-06-17 16:00:11,029 - sssd.utils.logger - INFO - Average MSE: 0.08462466970086098
2025-06-17 16:00:11,030 - sssd.utils.logger - INFO - Average MAPE: 0.614367663860321
2025-06-17 16:00:11,030 - sssd.utils.logger - INFO - Current time: 2025-06-17 16:00:11
Inference Job completed

Summary of the above results:

missing_kmaskingAvg MSEAvg MAPE
12forecast0.013520.27675
12rm0.016150.31982
12mnr0.023370.36655
12bm0.025710.47934
24forecast0.077420.24574
24rm0.031980.30045
24mnr0.031020.44636
24bm0.053750.50501
200forecast0.039380.29433
200rm0.101930.51333
200mnr0.090880.70561
200bm0.084620.61437

MSE

  • forecast masking consistently achieves the lowest MSE across almost all missing_k values (especially for k = 12 and 200).
  • MSE clearly increases as k increases, indicating that the proportion of missing data significantly affects model performance.

MAPE

  • k = 12 yields the best overall performance, with all MAPE values below 0.5.
  • forecast consistently achieves the lowest or second-lowest MAPE, indicating smaller prediction errors.
  • The MAPE for mnr and bm exceeds 0.6 when k = 200, suggesting weaker capability in handling large-scale missing data.
Execution result reference
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
RM: 12
Shape: (5, 26, 500)
NAs: 0
MSPE: 0.0008432089553375721
MSPE (per polluant): [0.00117824 0.00060066 0.00082218 0.00083799 0.00077697]
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 26/26 [00:13<00:00,  1.91it/s]
BM: 12
Shape: (5, 26, 500)
NAs: 0
MSPE: 0.0020280764532541835
MSPE (per polluant): [0.00090764 0.00658069 0.00088254 0.00101575 0.00075376]
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 26/26 [00:13<00:00,  1.92it/s]
MNR: 12
Shape: (5, 26, 500)
NAs: 0
MSPE: 0.001264052932918028
MSPE (per polluant): [0.00086441 0.00190658 0.00084853 0.00106907 0.00163167]
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 26/26 [00:13<00:00,  1.90it/s]
TF: 12
Shape: (5, 26, 500)
NAs: 0
MSPE: 0.001126299277898192
MSPE (per polluant): [0.00123651 0.00113123 0.00106289 0.00112292 0.00107795]
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 26/26 [00:13<00:00,  1.90it/s]
RM: 24
Shape: (5, 26, 500)
NAs: 0
MSPE: 0.0015890958435139959
MSPE (per polluant): [0.0015957  0.00190802 0.0015648  0.00145854 0.00141841]
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 26/26 [00:13<00:00,  1.90it/s]
BM: 24
Shape: (5, 26, 500)
NAs: 0
MSPE: 0.002633397703545542
MSPE (per polluant): [0.00131936 0.00105603 0.00687749 0.00148756 0.00242654]
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 26/26 [00:13<00:00,  1.91it/s]
MNR: 24
Shape: (5, 26, 500)
NAs: 0
MSPE: 0.001541527787880978
MSPE (per polluant): [0.00216814 0.00208698 0.00142031 0.00137693 0.00065529]
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 26/26 [00:13<00:00,  1.87it/s]
TF: 24
Shape: (5, 26, 500)
NAs: 0
MSPE: 0.0037722312727362637
MSPE (per polluant): [0.00387845 0.00379054 0.00373233 0.0036643  0.00379554]
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 26/26 [00:12<00:00,  2.01it/s]
RM: 200
Shape: (5, 26, 500)
NAs: 0
MSPE: 0.040819488247708764
MSPE (per polluant): [0.0354449  0.0493968  0.0451148  0.0328515  0.04128944]
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 26/26 [00:12<00:00,  2.02it/s]
BM: 200
Shape: (5, 26, 500)
NAs: 0
MSPE: 0.032062961408641066
MSPE (per polluant): [0.05920136 0.05211573 0.02009791 0.01980364 0.00909616]
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 26/26 [00:12<00:00,  2.09it/s]
MNR: 200
Shape: (5, 26, 500)
NAs: 0
MSPE: 0.03637180626470659
MSPE (per polluant): [0.06253705 0.06469499 0.01767138 0.01850751 0.0184481 ]
MSPE: 0.03637180626470659
MSPE (per polluant): [0.06253705 0.06469499 0.01767138 0.01850751 0.0184481 ]
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 26/26 [00:11<00:00,  2.18it/s] 
TF: 200
Shape: (5, 26, 500)
NAs: 0
MSPE: 0.01576908332060547
MSPE (per polluant): [0.01596002 0.01542586 0.0159918  0.01558863 0.01587911]
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 26/26 [00:12<00:00,  2.10it/s] 

The summary is as follows:

missing_kmaskingAvg MSPEMSPE per pollutant (“CO”, “NOx”, “O3”, “PM2.5”, “SO2”)
12RM0.000843[0.00118, 0.00060, 0.00082, 0.00084, 0.00078]
12BM0.002028[0.00091, 0.00658, 0.00088, 0.00102, 0.00075]
12MNR0.001264[0.00086, 0.00191, 0.00085, 0.00107, 0.00163]
12TF0.001126[0.00124, 0.00113, 0.00106, 0.00112, 0.00108]
24RM0.001589[0.00160, 0.00191, 0.00156, 0.00146, 0.00142]
24BM0.002633[0.00132, 0.00106, 0.00688, 0.00149, 0.00243]
24MNR0.001542[0.00217, 0.00209, 0.00142, 0.00138, 0.00066]
24TF0.003772[0.00388, 0.00379, 0.00373, 0.00366, 0.00380]
200RM0.040819[0.03544, 0.04940, 0.04511, 0.03285, 0.04129]
200BM0.032063[0.05920, 0.05212, 0.02010, 0.01980, 0.00910]
200MNR0.036372[0.06254, 0.06469, 0.01767, 0.01851, 0.01845]
200TF0.015769[0.01596, 0.01543, 0.01599, 0.01559, 0.01588]

12

bm

gallery_made_with_nanogallery2-12-bm

rm

gallery_made_with_nanogallery2-12-rm

mnr

gallery_made_with_nanogallery2-12-mnr

forecast

gallery_made_with_nanogallery2-12-forecast

24

bm

gallery_made_with_nanogallery2-24-bm

rm

gallery_made_with_nanogallery2-24-rm

mnr

gallery_made_with_nanogallery2-24-mnr

forecast

gallery_made_with_nanogallery2-24-forecast

200

bm

gallery_made_with_nanogallery2-200-bm

rm

gallery_made_with_nanogallery2-200-rm

mnr

gallery_made_with_nanogallery2-200-mnr

forecast

gallery_made_with_nanogallery2-200-forecast

Conclusion

In this work, we aimed to revise the data inference code to improve prediction accuracy. During the prediction process, due to the nature of the $MRTS$ composed of spatial coordinates, it is important that the order of the time series input stations matches the original setup to ensure better forecasting results. However, it is evident that the current integration of the spatial and temporal models suffers from significant incompatibility, resulting in poor predictive performance. Therefore, the integration approach needs to be reconsidered.

Environment

  • Local Operating System: Windows 11 24H2
    • Programming Language: Python 3.12.9
  • Computing Platform: National Center for High-Performance Computing (NCHC) – Taiwan AI Cloud
    • Operating System: Ubuntu
    • Miniconda
    • GPU: NVIDIA Tesla V100 32GB GPU
    • CUDA 12.8 driver
    • Programming Language: Python 3.10.16 for Linux

Further Learning

References