diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index b07f3c114a43e6e1f312e018911a76d93b5cb738..5a0129369c1da25e3d239aac9ee547a1076eab35 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -15,6 +15,7 @@ default:
     #- pip install torch --index-url https://download.pytorch.org/whl/cpu
     - pip install joblib scikit-learn
     - pip install matplotlib lmfit seaborn extra_data
+    - pip install dask
 
 stages:
   - test
diff --git a/pes_to_spec/bnn.py b/pes_to_spec/bnn.py
index 242a507481f322d87182a70239ae5df2d8bd51fc..b91d173e16a1e0f987c365f67385723202de8a41 100644
--- a/pes_to_spec/bnn.py
+++ b/pes_to_spec/bnn.py
@@ -526,6 +526,8 @@ class BNNModel(RegressorMixin, BaseEstimator):
 
         # train
         self.model.train()
+        if torch.cuda.is_available():
+            self.model = self.model.to('cuda')
         for epoch in range(self.n_epochs):
             meter = {k: AverageMeter(k, ':6.3f')
                     for k in ('loss', '-log(lkl)', '-log(prior)', '-log(hyper)', 'sigma', 'w.prec.')}
@@ -535,6 +537,10 @@ class BNNModel(RegressorMixin, BaseEstimator):
                             prefix="Epoch: [{}]".format(epoch))
             for i, batch in enumerate(loader):
                 x_b, y_b, w_b = batch
+                if torch.cuda.is_available():
+                    x_b = x_b.to('cuda')
+                    y_b = y_b.to('cuda')
+                    w_b = w_b.to('cuda')
                 y_b_pred = self.model(x_b)
 
                 nll = self.model.neg_log_likelihood(y_b_pred, y_b, w_b)
@@ -558,6 +564,8 @@ class BNNModel(RegressorMixin, BaseEstimator):
             self.model.prune()
 
         self.model.eval()
+        if torch.cuda.is_available():
+            self.model = self.model.to('cpu')
 
         return self
 
diff --git a/pes_to_spec/model.py b/pes_to_spec/model.py
index 389c0827bc759812fbdc28049f6301b37fc2dcd1..f4b677f1edb2b56ae9edec21715638b15ced3ffa 100644
--- a/pes_to_spec/model.py
+++ b/pes_to_spec/model.py
@@ -2,7 +2,9 @@ from __future__ import annotations
 
 from typing import Any, Dict, List, Optional, Union, Tuple, Literal
 
+import sys
 import joblib
+import dask.array as da
 
 import numpy as np
 import scipy
@@ -282,6 +284,7 @@ class SelectRelevantLowResolution(TransformerMixin, BaseEstimator):
         self.poly = poly
         self.mean = dict()
         self.std = dict()
+        self.pedestal = {ch: 0.0 for ch in self.channels}
 
     def transform(self, X: Dict[str, np.ndarray],
                   keep_dictionary_structure: bool=False,
@@ -309,9 +312,13 @@ class SelectRelevantLowResolution(TransformerMixin, BaseEstimator):
         if self.delta_tof is not None:
             first = max(0, self.tof_start - self.delta_tof)
             last = min(X[self.channels[0]].shape[1], self.tof_start + self.delta_tof)
-            y = {channel: [item[:, (first + delta):(last + delta)] for delta in pulse_spacing[channel]]
+            y = {channel: [item[:, (first + delta):(last + delta)] - self.pedestal[channel]
+                           for delta in pulse_spacing[channel]]
                  for channel, item in X.items()
                    if channel in self.channels}
+            # convert to Numpy if it is a Dask array
+            y = {ch: [item.compute() if isinstance(item, da.Array) else item for item in v]
+                 for ch, v in y.items()}
             # pad it with zeros, if we reach the edge of the array
             for channel in y.keys():
                 y[channel] = [np.pad(y[channel][j], ((0, 0), (0, 2*self.delta_tof - y[channel][j].shape[1])))
@@ -336,7 +343,10 @@ class SelectRelevantLowResolution(TransformerMixin, BaseEstimator):
         Returns: The index.
         """
         # reduce on channel and on train ID
-        sum_low_res = - np.mean(sum(list(X.values())), axis=0)
+        sum_low_res = np.mean(sum([-(v - self.pedestal[ch]) for ch, v in X.items()]), axis=0)
+        # convert to Numpy if it is a Dask array
+        if isinstance(sum_low_res, da.Array):
+            sum_low_res = sum_low_res.compute()
         axis = np.arange(0.0, sum_low_res.shape[0], 1.0)
         #widths = np.arange(10, 50, step=5)
         #peak_idx = find_peaks_cwt(sum_low_res, widths)
@@ -367,6 +377,12 @@ class SelectRelevantLowResolution(TransformerMixin, BaseEstimator):
 
         Returns: The object itself.
         """
+        # estimate pedestal
+        self.pedestal = {ch: np.mean(v[:10])
+                      for ch, v in X.items()}
+        # convert to Numpy if it is a Dask array
+        self.pedestal = {ch: v.compute() if isinstance(v, da.Array) else v
+                      for ch, v in self.pedestal.items()}
         self.tof_start = self.estimate_prompt_peak(X)
         X_tr = self.transform(X, keep_dictionary_structure=True)
         self.mean = {ch: np.mean(X_tr[ch], axis=0, keepdims=True)
@@ -863,6 +879,11 @@ class Model(TransformerMixin, BaseEstimator):
         print("Finding region-of-interest")
         self.x_select.fit(low_res_data)
         low_res_select = self.x_select.transform(low_res_data, pulse_energy=pulse_energy)
+        low_res_selected_dict = self.x_select.transform(low_res_data, keep_dictionary_structure=True)
+
+        # after this we do not need low_res_data anymore
+        del low_res_data
+
         # keep the number of pulses
         B, P, _ = low_res_select.shape
         low_res_select = low_res_select.reshape((B*P, -1))
@@ -873,6 +894,8 @@ class Model(TransformerMixin, BaseEstimator):
         cov = EllipticEnvelope(random_state=0).fit(lr_sums)
         good_low_res = cov.predict(lr_sums)
         filter_lr = (good_low_res > 0)
+        del cov
+
         low_res_filter = low_res_select[filter_hr & filter_lr, :]
         high_res_filter = high_res_data[filter_hr & filter_lr, :]
         weights_filter = weights
@@ -889,6 +912,7 @@ class Model(TransformerMixin, BaseEstimator):
         if len(n_components) > 0:
             n_components = n_components[0]
         n_components = max(600, n_components)
+        del pca_test
 
         print(f"Using {n_components} comp. for PES PCA.")
         self.x_model.set_params(pca__n_components=n_components)
@@ -903,6 +927,7 @@ class Model(TransformerMixin, BaseEstimator):
         if len(n_components_hr) > 0:
             n_components_hr = n_components_hr[0]
         n_components_hr = max(20, n_components_hr)
+        del pca_test
 
         print(f"Using {n_components_hr} comp. for grating spec. PCA.")
         self.y_model.set_params(pca__n_components=n_components_hr)
@@ -1002,12 +1027,10 @@ class Model(TransformerMixin, BaseEstimator):
             return high_res.reshape((B, P, -1))
 
         # for consistency check per channel
-        selection_model = self.x_select
-        low_res_selected = selection_model.transform(low_res_data, keep_dictionary_structure=True)
         for channel in self.get_channels():
-            B, P, _ = low_res_selected[channel].shape
+            B, P, _ = low_res_selected_dict[channel].shape
             print(f"Calculate PCA on {channel}")
-            low_pca = self.channel_pca[channel].fit_transform(low_res_selected[channel].reshape(B*P, -1))
+            low_pca = self.channel_pca[channel].fit_transform(low_res_selected_dict[channel].reshape(B*P, -1))
             self.ood[channel].fit(low_pca)
 
         print("End of fit.")
diff --git a/pyproject.toml b/pyproject.toml
index f2414a836d280c43bfef3a211ed0a868efa3d4ea..f96c3a470bbe808139a2028a5035e62927917adc 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -30,6 +30,7 @@ dependencies = [
           "lmfit",
           "scikit-learn>=1.2.0",
           "torch",
+          "dask",
           ]
 
 [project.optional-dependencies]